AbstractXMLDiff.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.xml;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.Reader;
import java.util.List;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.commons.io.input.ReaderInputStream;
import org.slf4j.Logger;
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Difference;
import org.custommonkey.xmlunit.XMLUnit;
import org.openspcoop2.utils.LoggerWrapperFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
/**
* Classe utilizzabile per ricerche effettuate tramite espressioni XQuery
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public abstract class AbstractXMLDiff {
@SuppressWarnings("unused")
private static Logger logger = LoggerWrapperFactory.getLogger(AbstractXMLDiff.class);
public static void setLogger(Logger logger) {
AbstractXMLDiff.logger = logger;
}
// Static options, la libreria XMLUnit permette di definire queste proprietà staticamente.
private static XMLDiffOptions xmlDiffOptions = new XMLDiffOptions();
private static XMLDiffImplType implType = XMLDiffImplType.XML_UNIT;
private static DocumentBuilderFactory dbf_org_w3c_dom_document_impl = null;
public static XMLDiffImplType getImplType() {
return AbstractXMLDiff.implType;
}
public static void setImplType(XMLDiffImplType implType) {
AbstractXMLDiff.implType = implType;
}
public static XMLDiffOptions getXmlDiffOptions() {
return AbstractXMLDiff.xmlDiffOptions;
}
public static void setXmlDiffOptions(XMLDiffOptions xmlDiffOptions) {
AbstractXMLDiff.xmlDiffOptions = xmlDiffOptions;
}
private static boolean initialized = false;
private static synchronized void initialize(AbstractXMLUtils instance) throws XMLException{
if(AbstractXMLDiff.initialized==false){
AbstractXMLDiff.dbf_org_w3c_dom_document_impl = instance.newDocumentBuilderFactory();
AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setNamespaceAware(true);
AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setCoalescing(AbstractXMLDiff.xmlDiffOptions.isIgnoreDiffBetweenTextAndCDATA());
AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setIgnoringElementContentWhitespace(AbstractXMLDiff.xmlDiffOptions.isIgnoreWhitespace());
AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setIgnoringComments(AbstractXMLDiff.xmlDiffOptions.isIgnoreComments());
if(XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
XMLUnit.setIgnoreAttributeOrder(true);
XMLUnit.setExpandEntityReferences(true);
XMLUnit.setIgnoreComments(AbstractXMLDiff.xmlDiffOptions.isIgnoreComments());
XMLUnit.setIgnoreDiffBetweenTextAndCDATA(AbstractXMLDiff.xmlDiffOptions.isIgnoreDiffBetweenTextAndCDATA());
XMLUnit.setIgnoreWhitespace(AbstractXMLDiff.xmlDiffOptions.isIgnoreWhitespace());
XMLUnit.setNormalize(AbstractXMLDiff.xmlDiffOptions.isNormalize());
XMLUnit.setNormalizeWhitespace(AbstractXMLDiff.xmlDiffOptions.isNormalize());
}
AbstractXMLDiff.initialized = true;
}
}
private static synchronized void _initialize(XMLDiffImplType implType,XMLDiffOptions xmlDiffOptions,AbstractXMLUtils instance) throws XMLException{
AbstractXMLDiff.implType = implType;
AbstractXMLDiff.xmlDiffOptions = xmlDiffOptions;
AbstractXMLDiff.initialize(instance);
}
/* ***** INIT METHOD (ha bisogno degli abstract method) ***** */
public void initialize(XMLDiffImplType implType,XMLDiffOptions xmlDiffOptions) throws XMLException{
AbstractXMLDiff._initialize(implType,xmlDiffOptions,this.getXMLUtils());
}
/* ***** ABSTRACT METHOD ***** */
public abstract AbstractXMLUtils getXMLUtils();
public abstract Element readXPathElement(Element contenutoAsElement);
public abstract void normalizeDocument(Document document);
/* ***** EXCEPTION METHOD ***** */
private String difference;
public String getDifferenceDetails() throws XMLException {
if(!XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
throw new XMLException("Difference details permit only on implementation: "+XMLDiffImplType.XML_UNIT);
}
return this.difference;
}
/* ***** ENGINE ***** */
private boolean _diff(Object original, Object compare) throws XMLException{
if(AbstractXMLDiff.initialized==false){
throw new XMLException("Library not initialized. Invoke initialize method");
}
if(XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
return this._diffXmlUnit(original, compare);
}
else if(XMLDiffImplType.ORG_W3C_DOM_DOCUMENT.equals(AbstractXMLDiff.implType)){
return this._diffW3cDomDocument(original, compare);
}
else{
throw new XMLException("Implementation ["+AbstractXMLDiff.implType+"] not supported");
}
}
private boolean _diffXmlUnit(Object original, Object compare) throws XMLException{
DetailedDiff diff = null;
if( (original instanceof Document) || (original instanceof Element) || (original instanceof Node) ){
// Ottengo anche il compare come Document e li confronto
Document docOriginal = this._getDiffW3cDomDocument(original, "original");
Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
normalizeDocument(docOriginal);
normalizeDocument(docCompare);
}
diff = new DetailedDiff(XMLUnit.compareXML(docOriginal,docCompare));
}
else if( (original instanceof Reader) && (compare instanceof Reader) ){
Reader rOriginal = (Reader) original;
Reader rCompare = (Reader) compare;
try{
diff = new DetailedDiff(XMLUnit.compareXML(rOriginal,rCompare));
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
else if( (original instanceof Reader) && (compare instanceof String) ){
Reader rOriginal = (Reader) original;
String sCompare = (String) compare;
try{
diff = new DetailedDiff(XMLUnit.compareXML(rOriginal,sCompare));
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
else if( (original instanceof String) && (compare instanceof Reader) ){
String sOriginal = (String) original;
Reader rCompare = (Reader) compare;
try{
diff = new DetailedDiff(XMLUnit.compareXML(sOriginal,rCompare));
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
else if( (original instanceof String) && (compare instanceof String) ){
String sOriginal = (String) original;
String sCompare = (String) compare;
try{
diff = new DetailedDiff(XMLUnit.compareXML(sOriginal,sCompare));
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}
}
else if(
( (original instanceof InputStream) || (original instanceof File) )
&&
( (compare instanceof InputStream) || (compare instanceof File) )
){
InputSource isOriginal = null;
InputSource isCompare = null;
FileInputStream finOriginal = null;
FileInputStream finCompare = null;
try{
if(original instanceof InputStream){
isOriginal = new InputSource((InputStream)original);
}
else if(original instanceof File){
finOriginal = new FileInputStream((File)original);
isOriginal = new InputSource(finOriginal);
}
if(compare instanceof InputStream){
isCompare = new InputSource((InputStream)compare);
}
else if(compare instanceof File){
finCompare = new FileInputStream((File)compare);
isCompare = new InputSource(finCompare);
}
diff = new DetailedDiff(XMLUnit.compareXML(isOriginal,isCompare));
}catch(Exception e){
throw new XMLException(e.getMessage(),e);
}finally{
try{
if(finOriginal!=null){
finOriginal.close();
}
}catch(Exception eClose){}
try{
if(finCompare!=null){
finCompare.close();
}
}catch(Exception eClose){
// close
}
}
}
else{
// Ottengo anche il compare come Document e li confronto
Document docOriginal = this._getDiffW3cDomDocument(original, "original");
Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
normalizeDocument(docOriginal);
normalizeDocument(docCompare);
}
diff = new DetailedDiff(XMLUnit.compareXML(docOriginal,docCompare));
}
if(diff!=null && diff.identical()==false){
StringBuilder bfDifferences = new StringBuilder();
List<?> allDifferences = diff.getAllDifferences();
bfDifferences.append("allDifferences: "+ allDifferences.size());
bfDifferences.append("\n");
int index = 1;
for (Object object : allDifferences) {
if(object instanceof Difference){
Difference d = (Difference) object;
bfDifferences.append("Diff-"+index+": "+d.toString());
bfDifferences.append("\n");
}
index++;
}
this.difference = bfDifferences.toString();
}
return diff!=null ? diff.identical() : false;
}
private boolean _diffW3cDomDocument(Object original, Object compare) throws XMLException{
Document docOriginal = this._getDiffW3cDomDocument(original, "original");
Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
normalizeDocument(docOriginal);
normalizeDocument(docCompare);
}
return docOriginal.isEqualNode(docCompare);
}
private Document _getDiffW3cDomDocument(Object o,String parameterName) throws XMLException{
if(o==null){
throw new XMLException("Object is null for parameter ["+parameterName+"]");
}
if(o instanceof Document){
return (Document) o;
}
else if(o instanceof Element){
return ((Element) o).getOwnerDocument();
}
else if(o instanceof Node){
return ((Node) o).getOwnerDocument();
}
else if(
(o instanceof String) ||
(o instanceof File) ||
(o instanceof InputStream) ||
(o instanceof Reader)
){
ReaderInputStream ris = null;
try{
DocumentBuilder db = AbstractXMLDiff.dbf_org_w3c_dom_document_impl.newDocumentBuilder();
if(o instanceof String){
byte [] b = ((String)o).getBytes();
return db.parse(new ByteArrayInputStream(b));
}
else if(o instanceof File){
return db.parse((File)o);
}
else if(o instanceof InputStream){
return db.parse((InputStream)o);
}
else if(o instanceof Reader){
Reader r = (Reader) o;
ris = ReaderInputStream.builder()
.setReader(r)
.setCharset(org.openspcoop2.utils.resources.Charset.UTF_8.getValue())
.get();
return db.parse( ris );
}
else{
throw new XMLException("Object type ["+o.getClass().getName()+"] not supported ?? for parameter ["+parameterName+"]");
}
}catch(Exception e){
throw new XMLException("Object type ["+o.getClass().getName()+"] parser error for parameter ["+parameterName+"]: "+e.getMessage(),e);
}finally{
try{
if(ris!=null){
ris.close();
}
}catch(Exception eClose){
// close
}
}
}
else{
throw new XMLException("Object type ["+o.getClass().getName()+"] not supported for parameter ["+parameterName+"]");
}
}
/* ***** PUBLIC METHOD (SRC as Node) ***** */
public boolean diff(String original, String compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(File original, File compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(InputStream original, InputStream compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(Reader original, Reader compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(Document original, Document compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(Element original, Element compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(Node original, Node compare)throws XMLException{
return this._diff(original, compare);
}
public boolean diff(Object original, Object compare)throws XMLException{
return this._diff(original, compare);
}
}