XSDUtils.java

/*
f * 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.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * Classe utilizzabile per ottenere informazioni sugli schemi
 *
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */

public class XSDUtils {

	private AbstractXMLUtils xmlUtils;
	public XSDUtils(AbstractXMLUtils xmlUtils){
		this.xmlUtils = xmlUtils;
	}



	// IS SCHEMA
	public boolean isXSDSchema(byte[]xsd) throws XMLException {
		try{
			if(!this.xmlUtils.isDocument(xsd)){
				return false;
			}
			Document docXML = this.xmlUtils.newDocument(xsd);
			Element elemXML = docXML.getDocumentElement();
			return this.isXSDSchema(elemXML);
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}
	public boolean isXSDSchema(Document xsd) throws XMLException {
		Element elemXML = xsd.getDocumentElement();
		return this.isXSDSchema(elemXML);
	}

	public boolean isXSDSchema(Element xsd) throws XMLException {
		return this.isXSDSchema((Node)xsd);
	}
	public boolean isXSDSchema(Node xsd) throws XMLException {
		try{
			if(xsd == null){
				throw new Exception("Schema xsd da verificare non definito");
			}
			//System.out.println("LOCAL["+xsd.getLocalName()+"]  NAMESPACE["+xsd.getNamespaceURI()+"]");
			if(!"schema".equals(xsd.getLocalName())){
				return false;
			}
			if(!"http://www.w3.org/2001/XMLSchema".equals(xsd.getNamespaceURI())){
				return false;
			}
			return true;
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}








	// TARGET NAMESPACE

	public String getTargetNamespace(byte[]xsd) throws XMLException {
		try{
			if(!this.xmlUtils.isDocument(xsd)){
				throw new Exception("Schema xsd non e' un documento valido");
			}
			Document docXML = this.xmlUtils.newDocument(xsd);
			Element elemXML = docXML.getDocumentElement();
			return this.getTargetNamespace(elemXML);
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}

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

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

	public String getTargetNamespace(Node xsd) throws XMLException {
		try{
			if(xsd == null){
				throw new Exception("Schema xsd non e' un documento valido");
			}
			//System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
			if(!"schema".equals(xsd.getLocalName())){
				throw new Exception("Root element non e' uno schema xsd ("+xsd.getLocalName()+")");
			}
			String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "targetNamespace");
			//System.out.println("TARGET["+targetNamespace+"]");
			return targetNamespace;
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}




	
	
	// SCHEMA LOCATION
	public void updateSchemaLocation(Node schemaImportInclude, String newLocation){
		
		//System.out.println("------------------------- updateSchemaLocation -------------------------------");
		
		if(schemaImportInclude!=null && schemaImportInclude.getAttributes()!=null && schemaImportInclude.getAttributes().getLength()>0){
			
//			try{
//				System.out.println(" PRIMA: "+this.xmlUtils.toString(schemaImportInclude));
//			}catch(Exception e){System.out.println("ERRORE PRIMA");}
			
			Attr oldSchemaLocation = (Attr) schemaImportInclude.getAttributes().getNamedItem("schemaLocation");
			this.xmlUtils.removeAttribute(oldSchemaLocation, (Element)schemaImportInclude);
			
//			try{
//				System.out.println(" REMOVE: "+this.xmlUtils.toString(schemaImportInclude));
//			}catch(Exception e){System.out.println("ERRORE REMOVE");}
			
			oldSchemaLocation.setValue(newLocation);
			this.xmlUtils.addAttribute(oldSchemaLocation, (Element)schemaImportInclude);
			
//			try{
//				System.out.println(" DOPO: "+this.xmlUtils.toString(schemaImportInclude));
//			}catch(Exception e){System.out.println("ERRORE DOPO");}
		}
		
	}
	
	
	
	
	
	// IMPORT
	public String getImportNamespace(Node xsd) throws XMLException {
		try{
			if(xsd == null){
				throw new Exception("Non e' un import valido");
			}
			//System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
			if(!"import".equals(xsd.getLocalName())){
				throw new Exception("Root element non e' un import di uno schema xsd ("+xsd.getLocalName()+")");
			}
			String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "namespace");
			//System.out.println("TARGET["+targetNamespace+"]");
			return targetNamespace;
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}
	public String getImportSchemaLocation(Node xsd) throws XMLException {
		try{
			if(xsd == null){
				throw new Exception("Non e' un import valido");
			}
			//System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
			if(!"import".equals(xsd.getLocalName())){
				throw new Exception("Root element non e' un import di uno schema xsd ("+xsd.getLocalName()+")");
			}
			String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "schemaLocation");
			//System.out.println("TARGET["+targetNamespace+"]");
			return targetNamespace;
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}
	
	public boolean isSchemaWithOnlyImports(Node xsd) throws XMLException {
		if(this.isXSDSchema(xsd)==false){
			throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
		}
		List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
		if(listChild==null || listChild.size()<=0){
			return false; // schema vuoto
		}
		boolean schemaConSoloImports = true;
		for (int j = 0; j < listChild.size(); j++) {
			if( !( 
					"import".equals(listChild.get(j).getLocalName()) 
					&& 
					"http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
					) 
				){
				schemaConSoloImports = false;
				break;
			}
		}
		return schemaConSoloImports;
	}


	// INCLUDE
	public String getIncludeSchemaLocation(Node xsd) throws XMLException {
		try{
			if(xsd == null){
				throw new Exception("Non e' un import valido");
			}
			//System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
			if(!"include".equals(xsd.getLocalName())){
				throw new Exception("Root element non e' un include di uno schema xsd ("+xsd.getLocalName()+")");
			}
			String targetNamespace = this.xmlUtils.getAttributeValue(xsd, "schemaLocation");
			//System.out.println("TARGET["+targetNamespace+"]");
			return targetNamespace;
		}catch(Exception e){
			throw new XMLException(e.getMessage(),e);
		}
	}
	
	public boolean isSchemaWithOnlyIncludes(Node xsd) throws XMLException {
		if(this.isXSDSchema(xsd)==false){
			throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
		}
		List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
		if(listChild==null || listChild.size()<=0){
			return false; // schema vuoto
		}
		boolean schemaConSoloIncludes = true;
		for (int j = 0; j < listChild.size(); j++) {
			if( !( 
					"include".equals(listChild.get(j).getLocalName()) 
					&& 
					"http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
					) 
				){
				schemaConSoloIncludes = false;
				break;
			}
		}
		return schemaConSoloIncludes;
	}
	
	
	
	// IMPORT - INCLUDE
	
	public boolean isSchemaWithOnlyImportsAndIncludes(Node xsd) throws XMLException {
		if(this.isXSDSchema(xsd)==false){
			throw new XMLException("Il parametro fornito non è uno schema"); // non è uno schmea
		}
		List<Node> listChild = this.xmlUtils.getNotEmptyChildNodes(xsd, false);
		if(listChild==null || listChild.size()<=0){
			return false; // schema vuoto
		}
		boolean schemaConSoloImportsAndIncludes = true;
		for (int j = 0; j < listChild.size(); j++) {
			if( !( 
					( "import".equals(listChild.get(j).getLocalName()) || "include".equals(listChild.get(j).getLocalName()) ) 
					&& 
					"http://www.w3.org/2001/XMLSchema".equals(listChild.get(j).getNamespaceURI())
					) 
				){
				schemaConSoloImportsAndIncludes = false;
				break;
			}
		}
		return schemaConSoloImportsAndIncludes;
	}



	// READ

	public List<Node> readImportsAndIncludes(Document xsd) throws XMLException{
		return this.readImportsIncludes(null,xsd,null, true, true);
	}
	public List<Node> readImports(Document xsd) throws XMLException{
		return this.readImportsIncludes(null,xsd,null, true, false);
	}
	public List<Node> readIncludes(Document xsd) throws XMLException{
		return this.readImportsIncludes(null,xsd,null, false, true);
	}
	public List<Node> readImportsAndIncludes(Element xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, true, true);
	}
	public List<Node> readImports(Element xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, true, false);
	}
	public List<Node> readIncludes(Element xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, false, true);
	}
	public List<Node> readImportsAndIncludes(String targetNamespaceSchema,Element xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, true);
	}
	public List<Node> readImports(String targetNamespaceSchema,Element xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, false);
	}
	public List<Node> readIncludes(String targetNamespaceSchema,Element xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, false, true);
	}
	public List<Node> readImportsAndIncludes(Node xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, true, true);
	}
	public List<Node> readImports(Node xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, true, false);
	}
	public List<Node> readIncludes(Node xsd) throws XMLException{
		return this.readImportsIncludes(null,null,xsd, false, true);
	}
	public List<Node> readImportsAndIncludes(String targetNamespaceSchema,Node xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, true);
	}
	public List<Node> readImports(String targetNamespaceSchema,Node xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, true, false);
	}
	public List<Node> readIncludes(String targetNamespaceSchema,Node xsd) throws XMLException{
		return this.readImportsIncludes(targetNamespaceSchema,null,xsd, false, true);
	}
	private List<Node> readImportsIncludes(String targetNamespaceSchema,Document xsdD,Node xsdE,boolean imports,boolean includes) throws XMLException{

		try{
			List<Node> nodes = new ArrayList<Node>();
			NodeList list = null;
			if(xsdD!=null){
				list = xsdD.getChildNodes(); 
			}else{
				list = xsdE.getChildNodes(); 
			}
			if(list!=null){
				for(int i=0; i<list.getLength(); i++){
					Node child = list.item(i);
					if("schema".equals(child.getLocalName())){

						if(targetNamespaceSchema==null){
							try{
								targetNamespaceSchema = this.getTargetNamespace(child);
							}catch(Exception e){}
						}

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

										if(targetNamespaceSchema==null){
											try{
												targetNamespaceSchema = this.getImportNamespace(childDefinition);
											}catch(Exception e){}
										}		

										childDefinition.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
										nodes.add(childDefinition);
									}
								}
								if(includes){
									if("include".equals(childDefinition.getLocalName())){
										childDefinition.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
										nodes.add(childDefinition);
									}
								}
							}
						}
					}
					else if("import".equals(child.getLocalName())){
						if(imports){

							if(targetNamespaceSchema==null){
								try{
									targetNamespaceSchema = this.getImportNamespace(child);
								}catch(Exception e){}
							}	

							child.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
							nodes.add(child);
						}
					}
					else if("include".equals(child.getLocalName())){
						if(includes){							
							if(targetNamespaceSchema == null){
								throw new XMLException("Lo schema esegue un include senza definire un targetNamespace.");
							}
							child.setUserData("TargetNamespaceSchema", targetNamespaceSchema, null);
							nodes.add(child);
						}
					}
				}
			}
			return nodes;
		}catch(Exception e){
			throw new XMLException("Riscontrato errore durante la lettura dello schema: "+e.getMessage(),e);
		}
	}







	// REMOVE

	public void removeImport(Document xsd,Node importNode){
		this.removeImports_engine(xsd,null,true,false,importNode);
	}
	public void removeInclude(Document xsd,Node includeNode){
		this.removeImports_engine(xsd,null,false,true,includeNode);
	}

	public void removeImport(Element xsd,Node importNode){
		this.removeImports_engine(null,xsd,true,false,importNode);
	}
	public void removeInclude(Element xsd,Node includeNode){
		this.removeImports_engine(null,xsd,false,true,includeNode);
	}

	public void removeImport(Node xsd,Node importNode){
		this.removeImports_engine(null,xsd,true,false,importNode);
	}
	public void removeInclude(Node xsd,Node includeNode){
		this.removeImports_engine(null,xsd,false,true,includeNode);
	}

	public void removeImports(Document xsd){
		this.removeImports_engine(xsd,null,true,false,null);
	}
	public void removeIncludes(Document xsd){
		this.removeImports_engine(xsd,null,false,true,null);
	}
	public void removeImportsAndIncludes(Document xsd){
		this.removeImports_engine(xsd,null,true,true,null);
	}

	public void removeImports(Element xsd){
		this.removeImports_engine(null,xsd,true,false,null);
	}
	public void removeIncludes(Element xsd){
		this.removeImports_engine(null,xsd,false,true,null);
	}
	public void removeImportsAndIncludes(Element xsd){
		this.removeImports_engine(null,xsd,true,true,null);
	}

	public void removeImports(Node xsd){
		this.removeImports_engine(null,xsd,true,false,null);
	}
	public void removeIncludes(Node xsd){
		this.removeImports_engine(null,xsd,false,true,null);
	}
	public void removeImportsAndIncludes(Node xsd){
		this.removeImports_engine(null,xsd,true,true,null);
	}

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

		NodeList list = null;
		if(xsdD!=null){
			list = xsdD.getChildNodes(); 
		}else{
			list = xsdE.getChildNodes(); 
		}
		if(list!=null){
			for(int i=0; i<list.getLength(); i++){
				Node child = list.item(i);
				if("schema".equals(child.getLocalName())){
					NodeList listDefinition = child.getChildNodes();
					if(listDefinition!=null){
						for(int j=0; j<listDefinition.getLength(); j++){
							Node childDefinition = listDefinition.item(j);
							if(imports){
								if("import".equals(childDefinition.getLocalName())){
									if(importIncludeNode==null){
										child.removeChild(childDefinition);
									}else{
										if(importIncludeNode.equals(childDefinition)){
											child.removeChild(childDefinition);
										}
									}
								}
							}
							if(includes){
								if("include".equals(childDefinition.getLocalName())){
									if(importIncludeNode==null){
										child.removeChild(childDefinition);
									}else{
										if(importIncludeNode.equals(childDefinition)){
											child.removeChild(childDefinition);
										}
									}
								}
							}
						}
					}
				}
				else if("import".equals(child.getLocalName())){
					if(imports){
						if(importIncludeNode==null){
							if(xsdD!=null){
								xsdD.removeChild(child);
							}else{
								xsdE.removeChild(child);
							}
						}else{
							if(importIncludeNode.equals(child)){
								if(xsdD!=null){
									xsdD.removeChild(child);
								}else{
									xsdE.removeChild(child);
								}
							}
						}
					}
				}
				else if("include".equals(child.getLocalName())){
					if(includes){
						if(importIncludeNode==null){
							if(xsdD!=null){
								xsdD.removeChild(child);
							}else{
								xsdE.removeChild(child);
							}
						}
						else{
							if(importIncludeNode.equals(child)){
								if(xsdD!=null){
									xsdD.removeChild(child);
								}else{
									xsdE.removeChild(child);
								}
							}
						}
					}
				}
			}
		}
	}
	
	
	
	
	
	
	
	
	
	// BUILD SCHEMA COLLECTION
		
	public XSDSchemaCollection buildSchemaCollection(Map<String, byte[]> resources,Map<String, String> mappingNamespaceLocations,Logger logger) throws XMLException {

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

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

			StringBuilder bfContenitori = new StringBuilder();
			StringBuilder bfImportNormali = new StringBuilder();

			int indexSystemId = 1;
			for (String targetNamespace : mappingNamespaceLocations.keySet()) {
				
				String locations = mappingNamespaceLocations.get(targetNamespace);
				String [] splitLocations = locations.split(" ");

				/* 
				 * Motivo
				 *   Perche è stato fatto questo codice sotto ??? : perche' altrimenti la validazione risulta dipendente dall'ordine degli import con stesso namespace.
				 *   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
				 *   Riassumendo:
				 * 
				 * b1.xsd:
				 *	<xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *	  ....
				 *	</xs:schema>
				 *
				 * b2.xsd:
				 *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     .....
				 *  </xs:schema>
				 * 
				 * a.xsd:
				 * 	<xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     <xs:import namespace="TNS_B" schemaLocation="b1.xsd" />
				 *	   <xs:import namespace="TNS_B" schemaLocation="b2.xsd" />
				 *     .....
				 *  </xs:schema>
				 *  
				 *  1) When I ran xmllint --schema a.xsd data.xml, I was presented with this warning:
				 *  	  a.xsd:4: element import: Schemas parser warning : Element '{http://www.w3.org/2001/XMLSchema}import':
				 *  		 Skipping import of schema located at 'b2.xsd' for the namespace 'TNS_B', 
				 *  	     since this namespace was already imported with the schema located at 'b1.xsd'.
				 * 	   The fact that the import of b2.xsd was skipped obviously leads to this error:
				 *          a.xsd:9: element attribute: Schemas parser error : DETTAGLIO SULL'ERRORE DELLA VALIDAZIONE FALLITA

				 *  2) VisualStudio (XML Spy): work
				 *  
				 *  3) Xerces-J non produce errore in fase di creazione dello schema.
				 *          Produce pero' lo stesso errore di validazione: 
				 *          a.xsd:9: element attribute: Schemas parser error : DETTAGLIO SULL'ERRORE DELLA VALIDAZIONE FALLITA
				 *          
				 *  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.
				 *  The precise meaning of <import> is a bit fuzzy when you read the W3C spec, possibly deliberately so. As a result, interpretations vary.
				 *  Some XML processors tolerate multiple <import> for the same namespace, and essentially amalgamate all of the schemaLocation into a single target.
				 *  Other processors are stricter, and decide that only one <import> per target namespace is valid. 
				 *  I think this is more correct, when you consider that schemaLocation is optional.
				 *  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, 
				 *  giving much the same error as xmllint does. 
				 *  XML Spy, on the other hand, is much more permissive (but then, XML Spy's validation is notoriously flaky)
				 *  To be safe, you should not have these multiple imports. 
				 *  A given namespace should have a single "master" document, which in turn has an <include> for each sub-document. 
				 *  This master is often highly artificial, acting only as a container. for these sub-documents.
				 *  From what I've seen, this generally consists of "best practise" for XML Schema when it comes to maximum tool compatibility, 
				 *  but some will argue that it's a hack that takes away from elegant schema design.
				 *  
				 *  CONCLUSIONI:
				 *  	Viene quindi usato l'approccio a contenitore suggerito.
				 *  
				 * openspcoop_system.xsd:
				 * 	<xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     <xs:include namespace="TNS_B" schemaLocation="b1.xsd" />
				 *	   <xs:include namespace="TNS_B" schemaLocation="b2.xsd" />
				 *  </xs:schema>
				 *  
				 * a.xsd:
				 * 	<xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     <xs:import namespace="TNS_B" schemaLocation="openspcoop_system.xsd" />
				 *     .....
				 *  </xs:schema>
				 *  
				 *  
				 *  
				 * Inoltre viene applicato un ulteriore accorgimento per superare il seguente problema:
				 *  
				 * types.xsd:
				 *	<xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *	  ....
				 *	</xs:schema>
				 *
				 * f1.xsd:
				 *  <xs:schema targetNamespace="TNS_B" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *  	<xs:import namespace="TNS_A" schemaLocation="types.xsd" />
				 *     .....
				 *  </xs:schema>
				 *
				 * f2.xsd:
				 *  <xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *  	<xs:include namespace="TNS_A" schemaLocation="types.xsd" />
				 *     .....
				 *  </xs:schema>
				 * 
				 * root.xsd:
				 * 	<xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     <xs:import namespace="TNS_B" schemaLocation="f1.xsd" />
				 *	   <xs:import namespace="TNS_A" schemaLocation="f2.xsd" />
				 *  </xs:schema>
				 *  
				 *  Nonostante sia stato seguito l'approccio a contenitore, durante l'utilizzo del root.xsd schema, si ottiene l'errore:
				 *  root.xsd:4: element import: Schemas parser warning : Element '{http://www.w3.org/2001/XMLSchema}import':
				 *  		 Skipping import of schema located at 'f2.xsd' for the namespace 'TNS_A', 
				 *  	     since this namespace was already imported with the schema located at 'types.xsd'.
				 *  
				 *  Questo perche' importando il file f1.xsd, a sua volta questo importava lo schema types.xsd che definisce lo stesso namespace usato dopo.
				 *  Se invece si inverte l'import nel file root.xsd tale problema non avviene poiche' al momento di importare f1.xsd, 
				 *  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):
				 *  
				 * root.xsd:
				 * 	<xs:schema targetNamespace="TNS_A" xmlns:xs="http://www.w3.org/2001/XMLSchema">
				 *     <xs:import namespace="TNS_A" schemaLocation="f2.xsd" />
				 *     <xs:import namespace="TNS_B" schemaLocation="f1.xsd" />
				 *  </xs:schema> 
				 *  
				 *  Soluzione: La classe contenitore conterra' il namespace che viene utilizzato da piu' schemi e li includera'. 
				 *  		   Dagli schemi inclusi nella classe contenitore vengono esclusi gli schemi che sono inclusi o importati in un altro schema xsd sempre con stesso namespace.
				 *  		   Di fatto quindi la classe contenitore conterra' solo le inclusioni degli schemi con stesso namespace che non vengono importati/inclusi da altri.
				 *  		   Tali schemi possono poi essere utilizzati anche in altri schemi con differenti namespace come nel caso di esempio sopra riportato. 
				 *  		   Siccome comunque saranno importati sicuramente anche nella classe contenitore, vengono definiti prima gli import delle classi contenitori e
				 *  		   solo dopo gli import 'normali'. 
				 **/

				if(splitLocations.length==1){
					bfImportNormali.append("\t<xsd:import namespace=\""+targetNamespace+"\" schemaLocation=\""+splitLocations[0]+"\" />\n");
				}
				else{
					String systemIdNewSchema = "System_OpenSPCoop_Id_"+indexSystemId+".xsd";
					indexSystemId++;
					bfContenitori.append("\t<xsd:import namespace=\""+targetNamespace+"\" schemaLocation=\""+systemIdNewSchema+"\" />\n");

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

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

							List<Node> imports = this.readImports(d);
							if(imports!=null){
								for (Node node : imports) {
									if(node!=null){
										String schemaLocation = this.getImportSchemaLocation(node);
										if(schemaLocation!=null){
											//System.out.println("IMPORT: "+schemaLocation);
											String baseLocation = getBaseNameXSDLocation(schemaLocation);
											//System.out.println("IMPORT-BASE: "+baseLocation);
											if(splitLocations[i].equals(baseLocation)){
												findImportInclude = true;
												break;
											}
										}
									}
								}
							}
							if(findImportInclude){
								break;
							}

							List<Node> includes = this.readIncludes(d);
							if(includes!=null){
								for (Node node : includes) {
									if(node!=null){
										String schemaLocation = this.getIncludeSchemaLocation(node);
										if(schemaLocation!=null){
											//System.out.println("INCLUDE: "+schemaLocation);
											String baseLocation = getBaseNameXSDLocation(schemaLocation);
											//System.out.println("INCLUDE-BASE: "+baseLocation);
											if(splitLocations[i].equals(baseLocation)){
												findImportInclude = true;
												break;
											}
										}
									}
								}
							}
							if(findImportInclude){
								break;
							}
						}

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

				}
			}
			bf.append(bfContenitori.toString());
			bf.append(bfImportNormali.toString());
			bf.append("</xsd:schema>");
			schemaPerValidazione = bf.toString().getBytes();
			//System.out.println("XSD: ["+bf.toString()+"]");

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

		XSDSchemaCollection collection = new XSDSchemaCollection();
		collection.setSchemaRoot(schemaPerValidazione);
		collection.setResources(resources);
		collection.setMappingNamespaceLocations(mappingNamespaceLocations);
		
		return collection;

	}
	
	
	
	
	
	// UTILITIES GENERICHE
	
	public void registraMappingNamespaceLocations(byte[] resource,String location,Map<String, String> mappingNamespaceLocations) throws XMLException {
		String targetNamespaceXSD = this.getTargetNamespace(resource);
		if(targetNamespaceXSD!=null){
			//System.out.println("ADD MAPPING PER ["+targetNamespaceXSD+"]...");
			if(mappingNamespaceLocations.containsKey(targetNamespaceXSD)){
				String locationGiaAssociataTargetNamespace = mappingNamespaceLocations.remove(targetNamespaceXSD);
				String newValue = locationGiaAssociataTargetNamespace+" "+location;
				mappingNamespaceLocations.put(targetNamespaceXSD, newValue);
				//System.out.println("ADJUNCT a ["+locationGiaAssociataTargetNamespace+"], new: ["+newValue+"]");
			}
			else{
				mappingNamespaceLocations.put(targetNamespaceXSD, location);
				//System.out.println("FIRST ADD");
			}
		}
	}
	
	public String getBaseNameXSDLocation(String location) throws MalformedURLException{
		if(location.startsWith("http://") || location.startsWith("https://") || location.startsWith("file://")){
			URL url = new URL(location);
			File fileUrl = new File(url.getFile());
			return fileUrl.getName();
		}
		else{
			File f = new File(location);
			return f.getName();
		}
	}
	

}