XSDUtils.java

  1. /*
  2. f * 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.File;
  22. import java.net.MalformedURLException;
  23. import java.net.URL;
  24. import java.util.ArrayList;
  25. import java.util.List;
  26. import java.util.Map;

  27. import org.slf4j.Logger;
  28. import org.w3c.dom.Attr;
  29. import org.w3c.dom.Document;
  30. import org.w3c.dom.Element;
  31. import org.w3c.dom.Node;
  32. import org.w3c.dom.NodeList;

  33. /**
  34.  * Classe utilizzabile per ottenere informazioni sugli schemi
  35.  *
  36.  *
  37.  * @author Poli Andrea (apoli@link.it)
  38.  * @author $Author$
  39.  * @version $Rev$, $Date$
  40.  */

  41. public class XSDUtils {

  42.     private AbstractXMLUtils xmlUtils;
  43.     public XSDUtils(AbstractXMLUtils xmlUtils){
  44.         this.xmlUtils = xmlUtils;
  45.     }



  46.     // IS SCHEMA
  47.     public boolean isXSDSchema(byte[]xsd) throws XMLException {
  48.         try{
  49.             if(!this.xmlUtils.isDocument(xsd)){
  50.                 return false;
  51.             }
  52.             Document docXML = this.xmlUtils.newDocument(xsd);
  53.             Element elemXML = docXML.getDocumentElement();
  54.             return this.isXSDSchema(elemXML);
  55.         }catch(Exception e){
  56.             throw new XMLException(e.getMessage(),e);
  57.         }
  58.     }
  59.     public boolean isXSDSchema(Document xsd) throws XMLException {
  60.         Element elemXML = xsd.getDocumentElement();
  61.         return this.isXSDSchema(elemXML);
  62.     }

  63.     public boolean isXSDSchema(Element xsd) throws XMLException {
  64.         return this.isXSDSchema((Node)xsd);
  65.     }
  66.     public boolean isXSDSchema(Node xsd) throws XMLException {
  67.         try{
  68.             if(xsd == null){
  69.                 throw new Exception("Schema xsd da verificare non definito");
  70.             }
  71.             //System.out.println("LOCAL["+xsd.getLocalName()+"]  NAMESPACE["+xsd.getNamespaceURI()+"]");
  72.             if(!"schema".equals(xsd.getLocalName())){
  73.                 return false;
  74.             }
  75.             if(!"http://www.w3.org/2001/XMLSchema".equals(xsd.getNamespaceURI())){
  76.                 return false;
  77.             }
  78.             return true;
  79.         }catch(Exception e){
  80.             throw new XMLException(e.getMessage(),e);
  81.         }
  82.     }








  83.     // TARGET NAMESPACE

  84.     public String getTargetNamespace(byte[]xsd) throws XMLException {
  85.         try{
  86.             if(!this.xmlUtils.isDocument(xsd)){
  87.                 throw new Exception("Schema xsd non e' un documento valido");
  88.             }
  89.             Document docXML = this.xmlUtils.newDocument(xsd);
  90.             Element elemXML = docXML.getDocumentElement();
  91.             return this.getTargetNamespace(elemXML);
  92.         }catch(Exception e){
  93.             throw new XMLException(e.getMessage(),e);
  94.         }
  95.     }

  96.     public String getTargetNamespace(Document xsd) throws XMLException {
  97.         Element elemXML = xsd.getDocumentElement();
  98.         return this.getTargetNamespace(elemXML);
  99.     }

  100.     public String getTargetNamespace(Element xsd) throws XMLException {
  101.         return this.getTargetNamespace((Node)xsd);
  102.     }

  103.     public String getTargetNamespace(Node xsd) throws XMLException {
  104.         try{
  105.             if(xsd == null){
  106.                 throw new Exception("Schema xsd non e' un documento valido");
  107.             }
  108.             //System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
  109.             if(!"schema".equals(xsd.getLocalName())){
  110.                 throw new Exception("Root element non e' uno schema xsd ("+xsd.getLocalName()+")");
  111.             }
  112.             String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "targetNamespace");
  113.             //System.out.println("TARGET["+targetNamespace+"]");
  114.             return targetNamespace;
  115.         }catch(Exception e){
  116.             throw new XMLException(e.getMessage(),e);
  117.         }
  118.     }




  119.    
  120.    
  121.     // SCHEMA LOCATION
  122.     public void updateSchemaLocation(Node schemaImportInclude, String newLocation){
  123.        
  124.         //System.out.println("------------------------- updateSchemaLocation -------------------------------");
  125.        
  126.         if(schemaImportInclude!=null && schemaImportInclude.getAttributes()!=null && schemaImportInclude.getAttributes().getLength()>0){
  127.            
  128. //          try{
  129. //              System.out.println(" PRIMA: "+this.xmlUtils.toString(schemaImportInclude));
  130. //          }catch(Exception e){System.out.println("ERRORE PRIMA");}
  131.            
  132.             Attr oldSchemaLocation = (Attr) schemaImportInclude.getAttributes().getNamedItem("schemaLocation");
  133.             this.xmlUtils.removeAttribute(oldSchemaLocation, (Element)schemaImportInclude);
  134.            
  135. //          try{
  136. //              System.out.println(" REMOVE: "+this.xmlUtils.toString(schemaImportInclude));
  137. //          }catch(Exception e){System.out.println("ERRORE REMOVE");}
  138.            
  139.             oldSchemaLocation.setValue(newLocation);
  140.             this.xmlUtils.addAttribute(oldSchemaLocation, (Element)schemaImportInclude);
  141.            
  142. //          try{
  143. //              System.out.println(" DOPO: "+this.xmlUtils.toString(schemaImportInclude));
  144. //          }catch(Exception e){System.out.println("ERRORE DOPO");}
  145.         }
  146.        
  147.     }
  148.    
  149.    
  150.    
  151.    
  152.    
  153.     // IMPORT
  154.     public String getImportNamespace(Node xsd) throws XMLException {
  155.         try{
  156.             if(xsd == null){
  157.                 throw new Exception("Non e' un import valido");
  158.             }
  159.             //System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
  160.             if(!"import".equals(xsd.getLocalName())){
  161.                 throw new Exception("Root element non e' un import di uno schema xsd ("+xsd.getLocalName()+")");
  162.             }
  163.             String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "namespace");
  164.             //System.out.println("TARGET["+targetNamespace+"]");
  165.             return targetNamespace;
  166.         }catch(Exception e){
  167.             throw new XMLException(e.getMessage(),e);
  168.         }
  169.     }
  170.     public String getImportSchemaLocation(Node xsd) throws XMLException {
  171.         try{
  172.             if(xsd == null){
  173.                 throw new Exception("Non e' un import valido");
  174.             }
  175.             //System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
  176.             if(!"import".equals(xsd.getLocalName())){
  177.                 throw new Exception("Root element non e' un import di uno schema xsd ("+xsd.getLocalName()+")");
  178.             }
  179.             String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "schemaLocation");
  180.             //System.out.println("TARGET["+targetNamespace+"]");
  181.             return targetNamespace;
  182.         }catch(Exception e){
  183.             throw new XMLException(e.getMessage(),e);
  184.         }
  185.     }
  186.    
  187.     public boolean isSchemaWithOnlyImports(Node xsd) throws XMLException {
  188.         if(this.isXSDSchema(xsd)==false){
  189.             throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
  190.         }
  191.         List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
  192.         if(listChild==null || listChild.size()<=0){
  193.             return false; // schema vuoto
  194.         }
  195.         boolean schemaConSoloImports = true;
  196.         for (int j = 0; j < listChild.size(); j++) {
  197.             if( !(
  198.                     "import".equals(listChild.get(j).getLocalName())
  199.                     &&
  200.                     "http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
  201.                     )
  202.                 ){
  203.                 schemaConSoloImports = false;
  204.                 break;
  205.             }
  206.         }
  207.         return schemaConSoloImports;
  208.     }


  209.     // INCLUDE
  210.     public String getIncludeSchemaLocation(Node xsd) throws XMLException {
  211.         try{
  212.             if(xsd == null){
  213.                 throw new Exception("Non e' un import valido");
  214.             }
  215.             //System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
  216.             if(!"include".equals(xsd.getLocalName())){
  217.                 throw new Exception("Root element non e' un include di uno schema xsd ("+xsd.getLocalName()+")");
  218.             }
  219.             String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "schemaLocation");
  220.             //System.out.println("TARGET["+targetNamespace+"]");
  221.             return targetNamespace;
  222.         }catch(Exception e){
  223.             throw new XMLException(e.getMessage(),e);
  224.         }
  225.     }
  226.    
  227.     public boolean isSchemaWithOnlyIncludes(Node xsd) throws XMLException {
  228.         if(this.isXSDSchema(xsd)==false){
  229.             throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
  230.         }
  231.         List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
  232.         if(listChild==null || listChild.size()<=0){
  233.             return false; // schema vuoto
  234.         }
  235.         boolean schemaConSoloIncludes = true;
  236.         for (int j = 0; j < listChild.size(); j++) {
  237.             if( !(
  238.                     "include".equals(listChild.get(j).getLocalName())
  239.                     &&
  240.                     "http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
  241.                     )
  242.                 ){
  243.                 schemaConSoloIncludes = false;
  244.                 break;
  245.             }
  246.         }
  247.         return schemaConSoloIncludes;
  248.     }
  249.    
  250.    
  251.    
  252.     // IMPORT - INCLUDE
  253.    
  254.     public boolean isSchemaWithOnlyImportsAndIncludes(Node xsd) throws XMLException {
  255.         if(this.isXSDSchema(xsd)==false){
  256.             throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
  257.         }
  258.         List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
  259.         if(listChild==null || listChild.size()<=0){
  260.             return false; // schema vuoto
  261.         }
  262.         boolean schemaConSoloImportsAndIncludes = true;
  263.         for (int j = 0; j < listChild.size(); j++) {
  264.             if( !(
  265.                     ( "import".equals(listChild.get(j).getLocalName()) || "include".equals(listChild.get(j).getLocalName()) )
  266.                     &&
  267.                     "http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
  268.                     )
  269.                 ){
  270.                 schemaConSoloImportsAndIncludes = false;
  271.                 break;
  272.             }
  273.         }
  274.         return schemaConSoloImportsAndIncludes;
  275.     }



  276.     // READ

  277.     public List<Node> readImportsAndIncludes(Document xsd) throws XMLException{
  278.         return this.readImportsIncludes(null,xsd,null, true, true);
  279.     }
  280.     public List<Node> readImports(Document xsd) throws XMLException{
  281.         return this.readImportsIncludes(null,xsd,null, true, false);
  282.     }
  283.     public List<Node> readIncludes(Document xsd) throws XMLException{
  284.         return this.readImportsIncludes(null,xsd,null, false, true);
  285.     }
  286.     public List<Node> readImportsAndIncludes(Element xsd) throws XMLException{
  287.         return this.readImportsIncludes(null,null,xsd, true, true);
  288.     }
  289.     public List<Node> readImports(Element xsd) throws XMLException{
  290.         return this.readImportsIncludes(null,null,xsd, true, false);
  291.     }
  292.     public List<Node> readIncludes(Element xsd) throws XMLException{
  293.         return this.readImportsIncludes(null,null,xsd, false, true);
  294.     }
  295.     public List<Node> readImportsAndIncludes(String targetNamespaceSchema,Element xsd) throws XMLException{
  296.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, true);
  297.     }
  298.     public List<Node> readImports(String targetNamespaceSchema,Element xsd) throws XMLException{
  299.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, false);
  300.     }
  301.     public List<Node> readIncludes(String targetNamespaceSchema,Element xsd) throws XMLException{
  302.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, false, true);
  303.     }
  304.     public List<Node> readImportsAndIncludes(Node xsd) throws XMLException{
  305.         return this.readImportsIncludes(null,null,xsd, true, true);
  306.     }
  307.     public List<Node> readImports(Node xsd) throws XMLException{
  308.         return this.readImportsIncludes(null,null,xsd, true, false);
  309.     }
  310.     public List<Node> readIncludes(Node xsd) throws XMLException{
  311.         return this.readImportsIncludes(null,null,xsd, false, true);
  312.     }
  313.     public List<Node> readImportsAndIncludes(String targetNamespaceSchema,Node xsd) throws XMLException{
  314.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, true);
  315.     }
  316.     public List<Node> readImports(String targetNamespaceSchema,Node xsd) throws XMLException{
  317.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, false);
  318.     }
  319.     public List<Node> readIncludes(String targetNamespaceSchema,Node xsd) throws XMLException{
  320.         return this.readImportsIncludes(targetNamespaceSchema,null,xsd, false, true);
  321.     }
  322.     private List<Node> readImportsIncludes(String targetNamespaceSchema,Document xsdD,Node xsdE,boolean imports,boolean includes) throws XMLException{

  323.         try{
  324.             List<Node> nodes = new ArrayList<Node>();
  325.             NodeList list = null;
  326.             if(xsdD!=null){
  327.                 list = xsdD.getChildNodes();
  328.             }else{
  329.                 list = xsdE.getChildNodes();
  330.             }
  331.             if(list!=null){
  332.                 for(int i=0; i<list.getLength(); i++){
  333.                     Node child = list.item(i);
  334.                     if("schema".equals(child.getLocalName())){

  335.                         if(targetNamespaceSchema==null){
  336.                             try{
  337.                                 targetNamespaceSchema = this.getTargetNamespace(child);
  338.                             }catch(Exception e){}
  339.                         }

  340.                         NodeList listDefinition = child.getChildNodes();
  341.                         if(listDefinition!=null){
  342.                             for(int j=0; j<listDefinition.getLength(); j++){
  343.                                 Node childDefinition = listDefinition.item(j);
  344.                                 if(imports){
  345.                                     if("import".equals(childDefinition.getLocalName())){

  346.                                         if(targetNamespaceSchema==null){
  347.                                             try{
  348.                                                 targetNamespaceSchema = this.getImportNamespace(childDefinition);
  349.                                             }catch(Exception e){}
  350.                                         }      

  351.                                         childDefinition.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
  352.                                         nodes.add(childDefinition);
  353.                                     }
  354.                                 }
  355.                                 if(includes){
  356.                                     if("include".equals(childDefinition.getLocalName())){
  357.                                         childDefinition.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
  358.                                         nodes.add(childDefinition);
  359.                                     }
  360.                                 }
  361.                             }
  362.                         }
  363.                     }
  364.                     else if("import".equals(child.getLocalName())){
  365.                         if(imports){

  366.                             if(targetNamespaceSchema==null){
  367.                                 try{
  368.                                     targetNamespaceSchema = this.getImportNamespace(child);
  369.                                 }catch(Exception e){}
  370.                             }  

  371.                             child.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
  372.                             nodes.add(child);
  373.                         }
  374.                     }
  375.                     else if("include".equals(child.getLocalName())){
  376.                         if(includes){                          
  377.                             if(targetNamespaceSchema == null){
  378.                                 throw new XMLException("Lo schema esegue un include senza definire un targetNamespace.");
  379.                             }
  380.                             child.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
  381.                             nodes.add(child);
  382.                         }
  383.                     }
  384.                 }
  385.             }
  386.             return nodes;
  387.         }catch(Exception e){
  388.             throw new XMLException("Riscontrato errore durante la lettura dello schema: "+e.getMessage(),e);
  389.         }
  390.     }







  391.     // REMOVE

  392.     public void removeImport(Document xsd,Node importNode){
  393.         this.removeImports_engine(xsd,null,true,false,importNode);
  394.     }
  395.     public void removeInclude(Document xsd,Node includeNode){
  396.         this.removeImports_engine(xsd,null,false,true,includeNode);
  397.     }

  398.     public void removeImport(Element xsd,Node importNode){
  399.         this.removeImports_engine(null,xsd,true,false,importNode);
  400.     }
  401.     public void removeInclude(Element xsd,Node includeNode){
  402.         this.removeImports_engine(null,xsd,false,true,includeNode);
  403.     }

  404.     public void removeImport(Node xsd,Node importNode){
  405.         this.removeImports_engine(null,xsd,true,false,importNode);
  406.     }
  407.     public void removeInclude(Node xsd,Node includeNode){
  408.         this.removeImports_engine(null,xsd,false,true,includeNode);
  409.     }

  410.     public void removeImports(Document xsd){
  411.         this.removeImports_engine(xsd,null,true,false,null);
  412.     }
  413.     public void removeIncludes(Document xsd){
  414.         this.removeImports_engine(xsd,null,false,true,null);
  415.     }
  416.     public void removeImportsAndIncludes(Document xsd){
  417.         this.removeImports_engine(xsd,null,true,true,null);
  418.     }

  419.     public void removeImports(Element xsd){
  420.         this.removeImports_engine(null,xsd,true,false,null);
  421.     }
  422.     public void removeIncludes(Element xsd){
  423.         this.removeImports_engine(null,xsd,false,true,null);
  424.     }
  425.     public void removeImportsAndIncludes(Element xsd){
  426.         this.removeImports_engine(null,xsd,true,true,null);
  427.     }

  428.     public void removeImports(Node xsd){
  429.         this.removeImports_engine(null,xsd,true,false,null);
  430.     }
  431.     public void removeIncludes(Node xsd){
  432.         this.removeImports_engine(null,xsd,false,true,null);
  433.     }
  434.     public void removeImportsAndIncludes(Node xsd){
  435.         this.removeImports_engine(null,xsd,true,true,null);
  436.     }

  437.     private void removeImports_engine(Document xsdD,Node xsdE,boolean imports,boolean includes,Node importIncludeNode){

  438.         NodeList list = null;
  439.         if(xsdD!=null){
  440.             list = xsdD.getChildNodes();
  441.         }else{
  442.             list = xsdE.getChildNodes();
  443.         }
  444.         if(list!=null){
  445.             for(int i=0; i<list.getLength(); i++){
  446.                 Node child = list.item(i);
  447.                 if("schema".equals(child.getLocalName())){
  448.                     NodeList listDefinition = child.getChildNodes();
  449.                     if(listDefinition!=null){
  450.                         for(int j=0; j<listDefinition.getLength(); j++){
  451.                             Node childDefinition = listDefinition.item(j);
  452.                             if(imports){
  453.                                 if("import".equals(childDefinition.getLocalName())){
  454.                                     if(importIncludeNode==null){
  455.                                         child.removeChild(childDefinition);
  456.                                     }else{
  457.                                         if(importIncludeNode.equals(childDefinition)){
  458.                                             child.removeChild(childDefinition);
  459.                                         }
  460.                                     }
  461.                                 }
  462.                             }
  463.                             if(includes){
  464.                                 if("include".equals(childDefinition.getLocalName())){
  465.                                     if(importIncludeNode==null){
  466.                                         child.removeChild(childDefinition);
  467.                                     }else{
  468.                                         if(importIncludeNode.equals(childDefinition)){
  469.                                             child.removeChild(childDefinition);
  470.                                         }
  471.                                     }
  472.                                 }
  473.                             }
  474.                         }
  475.                     }
  476.                 }
  477.                 else if("import".equals(child.getLocalName())){
  478.                     if(imports){
  479.                         if(importIncludeNode==null){
  480.                             if(xsdD!=null){
  481.                                 xsdD.removeChild(child);
  482.                             }else{
  483.                                 xsdE.removeChild(child);
  484.                             }
  485.                         }else{
  486.                             if(importIncludeNode.equals(child)){
  487.                                 if(xsdD!=null){
  488.                                     xsdD.removeChild(child);
  489.                                 }else{
  490.                                     xsdE.removeChild(child);
  491.                                 }
  492.                             }
  493.                         }
  494.                     }
  495.                 }
  496.                 else if("include".equals(child.getLocalName())){
  497.                     if(includes){
  498.                         if(importIncludeNode==null){
  499.                             if(xsdD!=null){
  500.                                 xsdD.removeChild(child);
  501.                             }else{
  502.                                 xsdE.removeChild(child);
  503.                             }
  504.                         }
  505.                         else{
  506.                             if(importIncludeNode.equals(child)){
  507.                                 if(xsdD!=null){
  508.                                     xsdD.removeChild(child);
  509.                                 }else{
  510.                                     xsdE.removeChild(child);
  511.                                 }
  512.                             }
  513.                         }
  514.                     }
  515.                 }
  516.             }
  517.         }
  518.     }
  519.    
  520.    
  521.    
  522.    
  523.    
  524.    
  525.    
  526.    
  527.    
  528.     // BUILD SCHEMA COLLECTION
  529.        
  530.     public XSDSchemaCollection buildSchemaCollection(Map<String, byte[]> resources,Map<String, List<String>> mappingNamespaceLocations,Logger logger) throws XMLException {

  531.         // ---------  Check esistenza almeno 1 schema ---------
  532.         if(resources.size()==0){
  533.             throw new XMLException("Schemi non presenti");
  534.         }

  535.         // ---------  Creo XSD di root da utilizzare per la validazione ---------
  536.         byte[] schemaPerValidazione = null;
  537.         try{
  538.             StringBuilder bf = new StringBuilder();
  539.             bf.append("<xsd:schema targetNamespace=\"http://openspcoop2.org/validazione_engine\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n");

  540.             StringBuilder bfContenitori = new StringBuilder();
  541.             StringBuilder bfImportNormali = new StringBuilder();

  542.             int indexSystemId = 1;
  543.             for (String targetNamespace : mappingNamespaceLocations.keySet()) {
  544.                
  545.                 List<String> splitLocations = mappingNamespaceLocations.get(targetNamespace);

  546.                 /*
  547.                  * Motivo
  548.                  *   Perche è stato fatto questo codice sotto ??? : perche' altrimenti la validazione risulta dipendente dall'ordine degli import con stesso namespace.
  549.                  *   O Meglio il secondo import viene ignorato. Perche? La spiegazione e' in http://stackoverflow.com/questions/4998063/one-xml-namespace-equals-one-and-only-one-schema-file
  550.                  *   Riassumendo:
  551.                  *
  552.                  * b1.xsd:
  553.                  *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  554.                  *    ....
  555.                  *  </xs:schema>
  556.                  *
  557.                  * b2.xsd:
  558.                  *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  559.                  *     .....
  560.                  *  </xs:schema>
  561.                  *
  562.                  * a.xsd:
  563.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  564.                  *     <xs:import namespace="TNS_B" schemaLocation="b1.xsd" />
  565.                  *     <xs:import namespace="TNS_B" schemaLocation="b2.xsd" />
  566.                  *     .....
  567.                  *  </xs:schema>
  568.                  *  
  569.                  *  1) When I ran xmllint --schema a.xsd data.xml, I was presented with this warning:
  570.                  *        a.xsd:4: element import: Schemas parser warning : Element '{http://www.w3.org/2001/XMLSchema}import':
  571.                  *           Skipping import of schema located at 'b2.xsd' for the namespace 'TNS_B',
  572.                  *           since this namespace was already imported with the schema located at 'b1.xsd'.
  573.                  *     The fact that the import of b2.xsd was skipped obviously leads to this error:
  574.                  *          a.xsd:9: element attribute: Schemas parser error : DETTAGLIO SULL'ERRORE DELLA VALIDAZIONE FALLITA

  575.                  *  2) VisualStudio (XML Spy): work
  576.                  *  
  577.                  *  3) Xerces-J non produce errore in fase di creazione dello schema.
  578.                  *          Produce pero' lo stesso errore di validazione:
  579.                  *          a.xsd:9: element attribute: Schemas parser error : DETTAGLIO SULL'ERRORE DELLA VALIDAZIONE FALLITA
  580.                  *          
  581.                  *  The crux of the problem here is what does it mean when you have two different <import> elements, when both of them refer to the same namespace.
  582.                  *  The precise meaning of <import> is a bit fuzzy when you read the W3C spec, possibly deliberately so. As a result, interpretations vary.
  583.                  *  Some XML processors tolerate multiple <import> for the same namespace, and essentially amalgamate all of the schemaLocation into a single target.
  584.                  *  Other processors are stricter, and decide that only one <import> per target namespace is valid.
  585.                  *  I think this is more correct, when you consider that schemaLocation is optional.
  586.                  *  In addition to the VS and xmllint examples you gave, Xerces-J is also super-strict, and ignores subsequent <import> for the same target namespace,
  587.                  *  giving much the same error as xmllint does.
  588.                  *  XML Spy, on the other hand, is much more permissive (but then, XML Spy's validation is notoriously flaky)
  589.                  *  To be safe, you should not have these multiple imports.
  590.                  *  A given namespace should have a single "master" document, which in turn has an <include> for each sub-document.
  591.                  *  This master is often highly artificial, acting only as a container. for these sub-documents.
  592.                  *  From what I've seen, this generally consists of "best practise" for XML Schema when it comes to maximum tool compatibility,
  593.                  *  but some will argue that it's a hack that takes away from elegant schema design.
  594.                  *  
  595.                  *  CONCLUSIONI:
  596.                  *      Viene quindi usato l'approccio a contenitore suggerito.
  597.                  *  
  598.                  * openspcoop_system.xsd:
  599.                  *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  600.                  *     <xs:include namespace="TNS_B" schemaLocation="b1.xsd" />
  601.                  *     <xs:include namespace="TNS_B" schemaLocation="b2.xsd" />
  602.                  *  </xs:schema>
  603.                  *  
  604.                  * a.xsd:
  605.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  606.                  *     <xs:import namespace="TNS_B" schemaLocation="openspcoop_system.xsd" />
  607.                  *     .....
  608.                  *  </xs:schema>
  609.                  *  
  610.                  *  
  611.                  *  
  612.                  * Inoltre viene applicato un ulteriore accorgimento per superare il seguente problema:
  613.                  *  
  614.                  * types.xsd:
  615.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  616.                  *    ....
  617.                  *  </xs:schema>
  618.                  *
  619.                  * f1.xsd:
  620.                  *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  621.                  *      <xs:import namespace="TNS_A" schemaLocation="types.xsd" />
  622.                  *     .....
  623.                  *  </xs:schema>
  624.                  *
  625.                  * f2.xsd:
  626.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  627.                  *      <xs:include namespace="TNS_A" schemaLocation="types.xsd" />
  628.                  *     .....
  629.                  *  </xs:schema>
  630.                  *
  631.                  * root.xsd:
  632.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  633.                  *     <xs:import namespace="TNS_B" schemaLocation="f1.xsd" />
  634.                  *     <xs:import namespace="TNS_A" schemaLocation="f2.xsd" />
  635.                  *  </xs:schema>
  636.                  *  
  637.                  *  Nonostante sia stato seguito l'approccio a contenitore, durante l'utilizzo del root.xsd schema, si ottiene l'errore:
  638.                  *  root.xsd:4: element import: Schemas parser warning : Element '{http://www.w3.org/2001/XMLSchema}import':
  639.                  *           Skipping import of schema located at 'f2.xsd' for the namespace 'TNS_A',
  640.                  *           since this namespace was already imported with the schema located at 'types.xsd'.
  641.                  *  
  642.                  *  Questo perche' importando il file f1.xsd, a sua volta questo importava lo schema types.xsd che definisce lo stesso namespace usato dopo.
  643.                  *  Se invece si inverte l'import nel file root.xsd tale problema non avviene poiche' al momento di importare f1.xsd,
  644.                  *  il namespace TNS_A e' gia' stato creato ed il file types.xsd gia' stato importato correttamente (avendo il solito nome di file puo' essere ignorato):
  645.                  *  
  646.                  * root.xsd:
  647.                  *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  648.                  *     <xs:import namespace="TNS_A" schemaLocation="f2.xsd" />
  649.                  *     <xs:import namespace="TNS_B" schemaLocation="f1.xsd" />
  650.                  *  </xs:schema>
  651.                  *  
  652.                  *  Soluzione: La classe contenitore conterra' il namespace che viene utilizzato da piu' schemi e li includera'.
  653.                  *             Dagli schemi inclusi nella classe contenitore vengono esclusi gli schemi che sono inclusi o importati in un altro schema xsd sempre con stesso namespace.
  654.                  *             Di fatto quindi la classe contenitore conterra' solo le inclusioni degli schemi con stesso namespace che non vengono importati/inclusi da altri.
  655.                  *             Tali schemi possono poi essere utilizzati anche in altri schemi con differenti namespace come nel caso di esempio sopra riportato.
  656.                  *             Siccome comunque saranno importati sicuramente anche nella classe contenitore, vengono definiti prima gli import delle classi contenitori e
  657.                  *             solo dopo gli import 'normali'.
  658.                  **/

  659.                 if(splitLocations.size()==1){
  660.                     bfImportNormali.append("\t<xsd:import namespace=\""+targetNamespace+"\" schemaLocation=\""+splitLocations.get(0)+"\" />\n");
  661.                 }
  662.                 else{
  663.                     String systemIdNewSchema = "System_OpenSPCoop_Id_"+indexSystemId+".xsd";
  664.                     indexSystemId++;
  665.                     bfContenitori.append("\t<xsd:import namespace=\""+targetNamespace+"\" schemaLocation=\""+systemIdNewSchema+"\" />\n");

  666.                     // Creo schema che contiene tutti gli schemi con stesso target namespace e registro la nuova risorsa
  667.                     StringBuilder bfStessoNamespace = new StringBuilder();
  668.                     bfStessoNamespace.append("<xsd:schema targetNamespace=\""+targetNamespace+"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">\n");
  669.                     for(int i=0;i<splitLocations.size();i++){

  670.                         // Devo includere uno schema solo se questo non e' gia' a sua volta incluso o importato da un altro schema con stesso namespace.
  671.                         // Altrimenti ottengo un errore simile al seguente:
  672.                         //  A schema cannot contain two global components with the same name; this schema contains two occurrences of ....
  673.                         boolean findImportInclude = false;
  674.                         for(int j=0;j<splitLocations.size();j++){
  675.                             if(j==i){
  676.                                 continue; // salto me stesso
  677.                             }
  678.                             byte [] xsd = resources.get(splitLocations.get(j));
  679.                             Document d = this.xmlUtils.newDocument(xsd);

  680.                             List<Node> imports = this.readImports(d);
  681.                             if(imports!=null){
  682.                                 for (Node node : imports) {
  683.                                     if(node!=null){
  684.                                         String schemaLocation = this.getImportSchemaLocation(node);
  685.                                         if(schemaLocation!=null){
  686.                                             //System.out.println("IMPORT: "+schemaLocation);
  687.                                             String baseLocation = getBaseNameXSDLocation(schemaLocation);
  688.                                             //System.out.println("IMPORT-BASE: "+baseLocation);
  689.                                             if(splitLocations.get(i).equals(baseLocation)){
  690.                                                 findImportInclude = true;
  691.                                                 break;
  692.                                             }
  693.                                         }
  694.                                     }
  695.                                 }
  696.                             }
  697.                             if(findImportInclude){
  698.                                 break;
  699.                             }

  700.                             List<Node> includes = this.readIncludes(d);
  701.                             if(includes!=null){
  702.                                 for (Node node : includes) {
  703.                                     if(node!=null){
  704.                                         String schemaLocation = this.getIncludeSchemaLocation(node);
  705.                                         if(schemaLocation!=null){
  706.                                             //System.out.println("INCLUDE: "+schemaLocation);
  707.                                             String baseLocation = getBaseNameXSDLocation(schemaLocation);
  708.                                             //System.out.println("INCLUDE-BASE: "+baseLocation);
  709.                                             if(splitLocations.get(i).equals(baseLocation)){
  710.                                                 findImportInclude = true;
  711.                                                 break;
  712.                                             }
  713.                                         }
  714.                                     }
  715.                                 }
  716.                             }
  717.                             if(findImportInclude){
  718.                                 break;
  719.                             }
  720.                         }

  721.                         if(findImportInclude==false)
  722.                             bfStessoNamespace.append("\t<xsd:include schemaLocation=\""+splitLocations.get(i)+"\" />\n");
  723.                     }
  724.                     bfStessoNamespace.append("</xsd:schema>");
  725.                     //System.out.println("NUOVA REGISTRAZIONE PER ["+systemIdNewSchema+"] ["+bfStessoNamespace.toString()+"]");
  726.                     resources.put(systemIdNewSchema, bfStessoNamespace.toString().getBytes());

  727.                 }
  728.             }
  729.             bf.append(bfContenitori.toString());
  730.             bf.append(bfImportNormali.toString());
  731.             bf.append("</xsd:schema>");
  732.             schemaPerValidazione = bf.toString().getBytes();
  733.             //System.out.println("XSD: ["+bf.toString()+"]");

  734.         }catch(Exception e){
  735.             throw new XMLException("Creazione dello schema fallita: "+e.getMessage(),e);
  736.         }

  737.         XSDSchemaCollection collection = new XSDSchemaCollection();
  738.         collection.setSchemaRoot(schemaPerValidazione);
  739.         collection.setResources(resources);
  740.         collection.setMappingNamespaceLocations(mappingNamespaceLocations);
  741.        
  742.         return collection;

  743.     }
  744.    
  745.    
  746.    
  747.    
  748.    
  749.     // UTILITIES GENERICHE
  750.    
  751.     public void registraMappingNamespaceLocations(byte[] resource,String location,Map<String, List<String>> mappingNamespaceLocations) throws XMLException {
  752.         String targetNamespaceXSD = this.getTargetNamespace(resource);
  753.         if(targetNamespaceXSD!=null){
  754.             mappingNamespaceLocations.computeIfAbsent(targetNamespaceXSD, k -> new ArrayList<>())
  755.                 .add(location);
  756.         }
  757.     }
  758.    
  759.     public String getBaseNameXSDLocation(String location) throws MalformedURLException{
  760.         if(location.startsWith("http://") || location.startsWith("https://") || location.startsWith("file://")){
  761.             URL url = new URL(location);
  762.             File fileUrl = new File(url.getFile());
  763.             return fileUrl.getName();
  764.         }
  765.         else{
  766.             File f = new File(location);
  767.             return f.getName();
  768.         }
  769.     }
  770.    

  771. }