AbstractXMLDiff.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.utils.xml;

  21. import java.io.ByteArrayInputStream;
  22. import java.io.File;
  23. import java.io.FileInputStream;
  24. import java.io.InputStream;
  25. import java.io.Reader;
  26. import java.util.List;

  27. import javax.xml.parsers.DocumentBuilder;
  28. import javax.xml.parsers.DocumentBuilderFactory;

  29. import org.apache.commons.io.input.ReaderInputStream;
  30. import org.slf4j.Logger;
  31. import org.custommonkey.xmlunit.DetailedDiff;
  32. import org.custommonkey.xmlunit.Difference;
  33. import org.custommonkey.xmlunit.XMLUnit;
  34. import org.openspcoop2.utils.LoggerWrapperFactory;
  35. import org.w3c.dom.Document;
  36. import org.w3c.dom.Element;
  37. import org.w3c.dom.Node;
  38. import org.xml.sax.InputSource;

  39. /**
  40.  * Classe utilizzabile per ricerche effettuate tramite espressioni XQuery
  41.  *
  42.  * @author Andrea Poli (apoli@link.it)
  43.  * @author $Author$
  44.  * @version $Rev$, $Date$
  45.  */

  46. public abstract class AbstractXMLDiff {

  47.    
  48.     @SuppressWarnings("unused")
  49.     private static Logger logger = LoggerWrapperFactory.getLogger(AbstractXMLDiff.class);
  50.     public static void setLogger(Logger logger) {
  51.         AbstractXMLDiff.logger = logger;
  52.     }
  53.    
  54.     // Static options, la libreria XMLUnit permette di definire queste proprietà staticamente.
  55.     private static XMLDiffOptions xmlDiffOptions = new XMLDiffOptions();
  56.     private static XMLDiffImplType implType = XMLDiffImplType.XML_UNIT;
  57.     private static DocumentBuilderFactory dbf_org_w3c_dom_document_impl = null;
  58.     public static XMLDiffImplType getImplType() {
  59.         return AbstractXMLDiff.implType;
  60.     }
  61.     public static void setImplType(XMLDiffImplType implType) {
  62.         AbstractXMLDiff.implType = implType;
  63.     }
  64.     public static XMLDiffOptions getXmlDiffOptions() {
  65.         return AbstractXMLDiff.xmlDiffOptions;
  66.     }
  67.     public static void setXmlDiffOptions(XMLDiffOptions xmlDiffOptions) {
  68.         AbstractXMLDiff.xmlDiffOptions = xmlDiffOptions;
  69.     }
  70.     private static boolean initialized = false;
  71.     private static synchronized void initialize(AbstractXMLUtils instance) throws XMLException{
  72.         if(AbstractXMLDiff.initialized==false){
  73.            
  74.             AbstractXMLDiff.dbf_org_w3c_dom_document_impl = instance.newDocumentBuilderFactory();
  75.             AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setNamespaceAware(true);
  76.             AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setCoalescing(AbstractXMLDiff.xmlDiffOptions.isIgnoreDiffBetweenTextAndCDATA());
  77.             AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setIgnoringElementContentWhitespace(AbstractXMLDiff.xmlDiffOptions.isIgnoreWhitespace());
  78.             AbstractXMLDiff.dbf_org_w3c_dom_document_impl.setIgnoringComments(AbstractXMLDiff.xmlDiffOptions.isIgnoreComments());
  79.            
  80.             if(XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
  81.                 XMLUnit.setIgnoreAttributeOrder(true);
  82.                 XMLUnit.setExpandEntityReferences(true);
  83.                 XMLUnit.setIgnoreComments(AbstractXMLDiff.xmlDiffOptions.isIgnoreComments());
  84.                 XMLUnit.setIgnoreDiffBetweenTextAndCDATA(AbstractXMLDiff.xmlDiffOptions.isIgnoreDiffBetweenTextAndCDATA());
  85.                 XMLUnit.setIgnoreWhitespace(AbstractXMLDiff.xmlDiffOptions.isIgnoreWhitespace());
  86.                 XMLUnit.setNormalize(AbstractXMLDiff.xmlDiffOptions.isNormalize());
  87.                 XMLUnit.setNormalizeWhitespace(AbstractXMLDiff.xmlDiffOptions.isNormalize());
  88.             }
  89.            
  90.             AbstractXMLDiff.initialized = true;
  91.         }
  92.     }
  93.     private static synchronized void _initialize(XMLDiffImplType implType,XMLDiffOptions xmlDiffOptions,AbstractXMLUtils instance) throws XMLException{
  94.         AbstractXMLDiff.implType = implType;
  95.         AbstractXMLDiff.xmlDiffOptions = xmlDiffOptions;
  96.         AbstractXMLDiff.initialize(instance);
  97.     }

  98.    
  99.     /* ***** INIT METHOD (ha bisogno degli abstract method) ***** */
  100.    
  101.     public void initialize(XMLDiffImplType implType,XMLDiffOptions xmlDiffOptions) throws XMLException{
  102.         AbstractXMLDiff._initialize(implType,xmlDiffOptions,this.getXMLUtils());
  103.     }
  104.    
  105.    
  106.     /* ***** ABSTRACT METHOD ***** */
  107.    
  108.     public abstract AbstractXMLUtils getXMLUtils();
  109.     public abstract Element readXPathElement(Element contenutoAsElement);
  110.     public abstract void normalizeDocument(Document document);

  111.    
  112.     /* ***** EXCEPTION METHOD ***** */

  113.     private String difference;
  114.    
  115.     public String getDifferenceDetails() throws XMLException {
  116.         if(!XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
  117.             throw new XMLException("Difference details permit only on implementation: "+XMLDiffImplType.XML_UNIT);
  118.         }
  119.         return this.difference;
  120.     }

  121.    
  122.    
  123.    
  124.        
  125.    
  126.     /* ***** ENGINE ***** */
  127.    
  128.     private boolean _diff(Object original, Object compare) throws XMLException{
  129.        
  130.         if(AbstractXMLDiff.initialized==false){
  131.             throw new XMLException("Library not initialized. Invoke initialize method");
  132.         }
  133.        
  134.         if(XMLDiffImplType.XML_UNIT.equals(AbstractXMLDiff.implType)){
  135.             return this._diffXmlUnit(original, compare);
  136.         }
  137.         else if(XMLDiffImplType.ORG_W3C_DOM_DOCUMENT.equals(AbstractXMLDiff.implType)){
  138.             return this._diffW3cDomDocument(original, compare);
  139.         }
  140.         else{
  141.             throw new XMLException("Implementation ["+AbstractXMLDiff.implType+"] not supported");
  142.         }

  143.     }
  144.    
  145.     private boolean _diffXmlUnit(Object original, Object compare) throws XMLException{
  146.        
  147.         DetailedDiff diff = null;
  148.        
  149.         if( (original instanceof Document) || (original instanceof Element) || (original instanceof Node) ){
  150.             // Ottengo anche il compare come Document e li confronto
  151.             Document docOriginal = this._getDiffW3cDomDocument(original, "original");
  152.             Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
  153.             if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
  154.                 normalizeDocument(docOriginal);
  155.                 normalizeDocument(docCompare);
  156.             }
  157.             diff = new DetailedDiff(XMLUnit.compareXML(docOriginal,docCompare));
  158.         }
  159.         else if( (original instanceof Reader) && (compare instanceof Reader) ){
  160.             Reader rOriginal = (Reader) original;
  161.             Reader rCompare = (Reader) compare;
  162.             try{
  163.                 diff = new DetailedDiff(XMLUnit.compareXML(rOriginal,rCompare));
  164.             }catch(Exception e){
  165.                 throw new XMLException(e.getMessage(),e);
  166.             }
  167.         }
  168.         else if( (original instanceof Reader) && (compare instanceof String) ){
  169.             Reader rOriginal = (Reader) original;
  170.             String sCompare = (String) compare;
  171.             try{
  172.                 diff = new DetailedDiff(XMLUnit.compareXML(rOriginal,sCompare));
  173.             }catch(Exception e){
  174.                 throw new XMLException(e.getMessage(),e);
  175.             }
  176.         }
  177.         else if( (original instanceof String) && (compare instanceof Reader) ){
  178.             String sOriginal = (String) original;
  179.             Reader rCompare = (Reader) compare;
  180.             try{
  181.                 diff = new DetailedDiff(XMLUnit.compareXML(sOriginal,rCompare));
  182.             }catch(Exception e){
  183.                 throw new XMLException(e.getMessage(),e);
  184.             }
  185.         }
  186.         else if( (original instanceof String) && (compare instanceof String) ){
  187.             String sOriginal = (String) original;
  188.             String sCompare = (String) compare;
  189.             try{
  190.                 diff = new DetailedDiff(XMLUnit.compareXML(sOriginal,sCompare));
  191.             }catch(Exception e){
  192.                 throw new XMLException(e.getMessage(),e);
  193.             }
  194.         }
  195.         else if(
  196.                 ( (original instanceof InputStream) || (original instanceof File) )
  197.                 &&
  198.                 ( (compare instanceof InputStream) || (compare instanceof File) )
  199.                 ){
  200.             InputSource isOriginal = null;
  201.             InputSource isCompare = null;
  202.             FileInputStream finOriginal = null;
  203.             FileInputStream finCompare = null;
  204.             try{
  205.                
  206.                 if(original instanceof InputStream){
  207.                     isOriginal = new InputSource((InputStream)original);
  208.                 }
  209.                 else if(original instanceof File){
  210.                     finOriginal = new FileInputStream((File)original);
  211.                     isOriginal = new InputSource(finOriginal);
  212.                 }
  213.                
  214.                 if(compare instanceof InputStream){
  215.                     isCompare = new InputSource((InputStream)compare);
  216.                 }
  217.                 else if(compare instanceof File){
  218.                     finCompare = new FileInputStream((File)compare);
  219.                     isCompare = new InputSource(finCompare);
  220.                 }
  221.                
  222.                 diff = new DetailedDiff(XMLUnit.compareXML(isOriginal,isCompare));
  223.                
  224.             }catch(Exception e){
  225.                 throw new XMLException(e.getMessage(),e);
  226.             }finally{
  227.                 try{
  228.                     if(finOriginal!=null){
  229.                         finOriginal.close();
  230.                     }
  231.                 }catch(Exception eClose){}
  232.                 try{
  233.                     if(finCompare!=null){
  234.                         finCompare.close();
  235.                     }
  236.                 }catch(Exception eClose){
  237.                     // close
  238.                 }
  239.             }
  240.         }
  241.         else{
  242.             // Ottengo anche il compare come Document e li confronto
  243.             Document docOriginal = this._getDiffW3cDomDocument(original, "original");
  244.             Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
  245.             if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
  246.                 normalizeDocument(docOriginal);
  247.                 normalizeDocument(docCompare);
  248.             }
  249.             diff = new DetailedDiff(XMLUnit.compareXML(docOriginal,docCompare));
  250.         }
  251.        
  252.        
  253.         if(diff!=null && diff.identical()==false){
  254.             StringBuilder bfDifferences = new StringBuilder();
  255.             List<?> allDifferences = diff.getAllDifferences();
  256.             bfDifferences.append("allDifferences: "+ allDifferences.size());
  257.             bfDifferences.append("\n");
  258.             int index = 1;
  259.             for (Object object : allDifferences) {
  260.                  if(object instanceof Difference){
  261.                      Difference d = (Difference) object;
  262.                      bfDifferences.append("Diff-"+index+": "+d.toString());
  263.                      bfDifferences.append("\n");
  264.                  }
  265.                  index++;
  266.             }          
  267.             this.difference = bfDifferences.toString();
  268.         }
  269.         return diff!=null ? diff.identical() : false;
  270.        
  271.     }
  272.    
  273.     private boolean _diffW3cDomDocument(Object original, Object compare) throws XMLException{
  274.        
  275.         Document docOriginal = this._getDiffW3cDomDocument(original, "original");
  276.         Document docCompare = this._getDiffW3cDomDocument(compare, "compare");
  277.         if(AbstractXMLDiff.xmlDiffOptions.isNormalize()){
  278.             normalizeDocument(docOriginal);
  279.             normalizeDocument(docCompare);
  280.         }
  281.         return docOriginal.isEqualNode(docCompare);
  282.     }
  283.    
  284.     private Document _getDiffW3cDomDocument(Object o,String parameterName) throws XMLException{
  285.        
  286.         if(o==null){
  287.             throw new XMLException("Object is null for parameter ["+parameterName+"]");
  288.         }
  289.        
  290.         if(o instanceof Document){
  291.             return (Document) o;
  292.         }
  293.         else if(o instanceof Element){
  294.             return ((Element) o).getOwnerDocument();
  295.         }
  296.         else if(o instanceof Node){
  297.             return ((Node) o).getOwnerDocument();
  298.         }
  299.         else if(
  300.                 (o instanceof String) ||
  301.                 (o instanceof File) ||
  302.                 (o instanceof InputStream) ||
  303.                 (o instanceof Reader)
  304.                 ){
  305.             ReaderInputStream ris = null;
  306.             try{
  307.                 DocumentBuilder db = AbstractXMLDiff.dbf_org_w3c_dom_document_impl.newDocumentBuilder();
  308.                 if(o instanceof String){
  309.                     byte [] b = ((String)o).getBytes();
  310.                     return db.parse(new ByteArrayInputStream(b));
  311.                 }
  312.                 else if(o instanceof File){
  313.                     return db.parse((File)o);
  314.                 }
  315.                 else if(o instanceof InputStream){
  316.                     return db.parse((InputStream)o);
  317.                 }
  318.                 else if(o instanceof Reader){
  319.                     Reader r = (Reader) o;
  320.                     ris = ReaderInputStream.builder()
  321.                             .setReader(r)
  322.                             .setCharset(org.openspcoop2.utils.resources.Charset.UTF_8.getValue())
  323.                             .get();
  324.                     return db.parse( ris );
  325.                 }
  326.                 else{
  327.                     throw new XMLException("Object type ["+o.getClass().getName()+"] not supported ?? for parameter ["+parameterName+"]");
  328.                 }
  329.             }catch(Exception e){
  330.                 throw new XMLException("Object type ["+o.getClass().getName()+"] parser error for parameter ["+parameterName+"]: "+e.getMessage(),e);
  331.             }finally{
  332.                 try{
  333.                     if(ris!=null){
  334.                         ris.close();
  335.                     }
  336.                 }catch(Exception eClose){
  337.                     // close
  338.                 }
  339.             }
  340.         }
  341.         else{
  342.             throw new XMLException("Object type ["+o.getClass().getName()+"] not supported for parameter ["+parameterName+"]");
  343.         }
  344.     }

  345.    
  346.    
  347.     /* ***** PUBLIC METHOD (SRC as Node) ***** */
  348.    
  349.     public boolean diff(String original, String compare)throws XMLException{
  350.         return this._diff(original, compare);
  351.     }
  352.     public boolean diff(File original, File compare)throws XMLException{
  353.         return this._diff(original, compare);
  354.     }
  355.     public boolean diff(InputStream original, InputStream compare)throws XMLException{
  356.         return this._diff(original, compare);
  357.     }
  358.     public boolean diff(Reader original, Reader compare)throws XMLException{
  359.         return this._diff(original, compare);
  360.     }
  361.     public boolean diff(Document original, Document compare)throws XMLException{
  362.         return this._diff(original, compare);
  363.     }
  364.     public boolean diff(Element original, Element compare)throws XMLException{
  365.         return this._diff(original, compare);
  366.     }
  367.     public boolean diff(Node original, Node compare)throws XMLException{
  368.         return this._diff(original, compare);
  369.     }
  370.     public boolean diff(Object original, Object compare)throws XMLException{
  371.         return this._diff(original, compare);
  372.     }


  373. }