package org.openxdm.xcap.common.xml;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
public class SchemaContext {
/**
* Map with schemas as DOM documents, indexed by schema's target namespace
*/
private Map<String,Document> documentMap = new HashMap<String,Document>();
private SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
/**
* Retreives an instance from all schema files in a dir. The schema files must have the xsd file extension
* @return
* @throws IOException
* @throws SAXException
* @throws ParserConfigurationException
*/
public static SchemaContext fromDir(URI dirURI) throws SAXException, IOException, ParserConfigurationException {
// init dom resources
List<Document> schemaDocuments = new ArrayList<Document>();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
// read files and parse to dom resources
File schemaDir = new File(dirURI);
if(schemaDir.isDirectory()) {
// create filter to select only files with name that ends with .xsd
FileFilter fileFilter = new FileFilter() {
public boolean accept(File f) {
if(f.isDirectory()) {
return false;
}
else {
if(f.getName().endsWith(".xsd")){
return true;
}
else {
return false;
}
}
}
};
// get schema files from schema dir
File[] schemaFiles = schemaDir.listFiles(fileFilter);
for(File schemaFile : schemaFiles) {
// parse each one to dom document and add to schema docs list
schemaDocuments.add(documentBuilder.parse(schemaFile));
}
}
// create and return new schema context
return new SchemaContext(schemaDocuments);
}
/**
* Creates a new instance to provide schemas that can combine the ones specified as DOM documents.
* @param documents the list os schemas that can be combined by the provider
*/
public SchemaContext(List<Document> documents) {
for(Iterator<Document> i=documents.iterator();i.hasNext();) {
Document document = i.next();
String targetNamespace = (document.getDocumentElement()).getAttribute("targetNamespace");
if (targetNamespace != null) {
documentMap.put(targetNamespace,document);
}
}
}
public Schema getCombinedSchema(String rootTargetNamespace) {
// create temp list that will hold all schema docs sources to combine
LinkedList<DOMSource> sourcesToCombine = new LinkedList<DOMSource>();
// create temp list that will hold all schema docs to process
LinkedList<String> documentsToProcessByTargetNamepsace = new LinkedList<String>();
// get root document and kick off the process to find other needed schemas
documentsToProcessByTargetNamepsace.addLast(rootTargetNamespace);
// add others by looking at each document for others needed to import
while(!documentsToProcessByTargetNamepsace.isEmpty()) {
// get head target namespace
String targetNamespace = documentsToProcessByTargetNamepsace.removeFirst();
// get the related schema document
Document document = documentMap.get(targetNamespace);
if(document != null) {
// document exists, add source to combination list
sourcesToCombine.addFirst(new DOMSource(document));
// and find other docs to import
NodeList nl = document.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema","import");
for (int i=0;i<nl.getLength();i++){
Element elem = (Element)nl.item(i);
// found one, add target namespace to process list
documentsToProcessByTargetNamepsace.addLast(elem.getAttribute("namespace"));
}
}
else {
// document needed is not here, abort
return null;
}
}
// create a schema by combining all selected
try {
return factory.newSchema(sourcesToCombine.toArray(new DOMSource[sourcesToCombine.size()]));
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
}