package org.molgenis.util;
import org.apache.commons.io.IOUtils;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.xml.sax.SAXException;
import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
/**
* Compiles a xsd. Searches on the classpath for the xsd. You don't need to specify the whole path, just the name.
* Example: <code>new SchemaLoader("EMeasure.xsd")</code>
*
* @author erwin
*/
public class SchemaLoader implements LSResourceResolver
{
private Schema schema;
public SchemaLoader(String schemaName)
{
try
{
Resource schemaResource = getSchema(schemaName);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setResourceResolver(this);
schema = schemaFactory.newSchema(new StreamSource(schemaResource.getInputStream()));
}
catch (SAXException e)
{
throw new RuntimeException("Could not load schemas", e);
}
catch (IOException e)
{
throw new RuntimeException("Could not load schemas", e);
}
}
public SchemaLoader(InputStream is)
{
try
{
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
schemaFactory.setResourceResolver(this);
schema = schemaFactory.newSchema(new StreamSource(is));
}
catch (SAXException e)
{
throw new RuntimeException("Could not load schemas", e);
}
}
public Schema getSchema()
{
return schema;
}
private Resource getSchema(String schemaName) throws IOException
{
if (schemaName.contains("/"))
{
schemaName = schemaName.substring(schemaName.lastIndexOf('/'));
}
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String searchPattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + "/**/" + schemaName;
Resource[] resources = resourcePatternResolver.getResources(searchPattern);
if ((resources == null) || (resources.length == 0))
{
throw new RuntimeException("Could not find schema [" + schemaName + "]");
}
return resources[0];
}
@Override
public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI)
{
InputStream resourceAsStream;
try
{
resourceAsStream = getSchema(systemId).getInputStream();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
return new LSInputImpl(publicId, systemId, resourceAsStream);
}
protected static class LSInputImpl implements LSInput
{
private String publicId;
private String systemId;
@Override
public String getPublicId()
{
return publicId;
}
@Override
public void setPublicId(String publicId)
{
this.publicId = publicId;
}
@Override
public String getBaseURI()
{
return null;
}
@Override
public InputStream getByteStream()
{
return null;
}
@Override
public boolean getCertifiedText()
{
return false;
}
@Override
public Reader getCharacterStream()
{
return null;
}
@Override
public String getEncoding()
{
return null;
}
@Override
public String getStringData()
{
synchronized (inputStream)
{
try
{
return IOUtils.toString(inputStream, "UTF-8");
}
catch (IOException e)
{
throw new RuntimeException(e);
}
}
}
@Override
public void setBaseURI(String baseURI)
{
}
@Override
public void setByteStream(InputStream byteStream)
{
}
@Override
public void setCertifiedText(boolean certifiedText)
{
}
@Override
public void setCharacterStream(Reader characterStream)
{
}
@Override
public void setEncoding(String encoding)
{
}
@Override
public void setStringData(String stringData)
{
}
@Override
public String getSystemId()
{
return systemId;
}
@Override
public void setSystemId(String systemId)
{
this.systemId = systemId;
}
public BufferedInputStream getInputStream()
{
return inputStream;
}
public void setInputStream(BufferedInputStream inputStream)
{
this.inputStream = inputStream;
}
private BufferedInputStream inputStream;
public LSInputImpl(String publicId, String sysId, InputStream input)
{
this.publicId = publicId;
this.systemId = sysId;
this.inputStream = new BufferedInputStream(input);
}
}
}