com.thaiopensource.validate.schematron.SchemaReaderImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of wicketstuff-jing Show documentation
Show all versions of wicketstuff-jing Show documentation
Jing is a validator for RELAX NG and other schema languages. This
project was taken from http://code.google.com/p/jing-trang and
mavenized for inclusion in the Wicket Stuff HTML Validator.
The code was taken from the 20091111 release.
package com.thaiopensource.validate.schematron;
import com.thaiopensource.util.Localizer;
import com.thaiopensource.util.PropertyId;
import com.thaiopensource.util.PropertyMap;
import com.thaiopensource.util.PropertyMapBuilder;
import com.thaiopensource.validate.AbstractSchemaReader;
import com.thaiopensource.validate.IncorrectSchemaException;
import com.thaiopensource.validate.Option;
import com.thaiopensource.validate.ResolverFactory;
import com.thaiopensource.validate.Schema;
import com.thaiopensource.validate.ValidateProperty;
import com.thaiopensource.validate.Validator;
import com.thaiopensource.validate.prop.rng.RngProperty;
import com.thaiopensource.validate.prop.schematron.SchematronProperty;
import com.thaiopensource.validate.rng.CompactSchemaReader;
import com.thaiopensource.xml.sax.CountingErrorHandler;
import com.thaiopensource.xml.sax.DelegatingContentHandler;
import com.thaiopensource.xml.sax.DraconianErrorHandler;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.SourceLocator;
import javax.xml.transform.Templates;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXResult;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TemplatesHandler;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.io.InputStream;
class SchemaReaderImpl extends AbstractSchemaReader {
static final String SCHEMATRON_URI = "http://www.ascc.net/xml/schematron";
private static final String LOCATION_URI = "http://www.thaiopensource.com/ns/location";
private static final String ERROR_URI = "http://www.thaiopensource.com/ns/error";
private final Localizer localizer = new Localizer(SchemaReaderImpl.class);
private final Class extends SAXTransformerFactory> transformerFactoryClass;
private final TransformerFactoryInitializer transformerFactoryInitializer;
private final Templates schematron;
private final Schema schematronSchema;
private static final String SCHEMATRON_SCHEMA = "schematron.rnc";
private static final String SCHEMATRON_STYLESHEET = "schematron.xsl";
// XSLTC has some problems with extension functions and function-available, so
// we need a separate stylesheet. See
// https://issues.apache.org/jira/browse/XALANJ-2464
// https://issues.apache.org/jira/browse/XALANJ-2465
private static final String SCHEMATRON_XSLTC_STYLESHEET = "schematron-xsltc.xsl";
private static final PropertyId>[] supportedPropertyIds = {
ValidateProperty.ERROR_HANDLER,
ValidateProperty.XML_READER_CREATOR,
ValidateProperty.ENTITY_RESOLVER,
ValidateProperty.URI_RESOLVER,
ValidateProperty.RESOLVER,
SchematronProperty.DIAGNOSE,
SchematronProperty.PHASE,
};
SchemaReaderImpl(SAXTransformerFactory transformerFactory, TransformerFactoryInitializer transformerFactoryInitializer)
throws TransformerConfigurationException, IncorrectSchemaException {
this.transformerFactoryClass = transformerFactory.getClass();
this.transformerFactoryInitializer = transformerFactoryInitializer;
final boolean isXsltc = isXsltc(transformerFactoryClass);
final String stylesheet = isXsltc ? SCHEMATRON_XSLTC_STYLESHEET : SCHEMATRON_STYLESHEET;
final String resourceName = fullResourceName(stylesheet);
final StreamSource source = new StreamSource(getResourceAsStream(resourceName));
initTransformerFactory(transformerFactory);
schematron = transformerFactory.newTemplates(source);
InputSource schemaSource = new InputSource(getResourceAsStream(fullResourceName(SCHEMATRON_SCHEMA)));
PropertyMapBuilder builder = new PropertyMapBuilder();
builder.put(ValidateProperty.ERROR_HANDLER, new DraconianErrorHandler());
RngProperty.CHECK_ID_IDREF.add(builder);
try {
schematronSchema = CompactSchemaReader.getInstance().createSchema(schemaSource, builder.toPropertyMap());
}
catch (SAXException e) {
throw new IncorrectSchemaException();
}
catch (IOException e) {
throw new IncorrectSchemaException();
}
}
static boolean isXsltc(Class extends SAXTransformerFactory> cls) {
return cls.getName().indexOf(".xsltc.") >= 0;
}
public Option getOption(String uri) {
return SchematronProperty.getOption(uri);
}
private void initTransformerFactory(TransformerFactory factory) {
transformerFactoryInitializer.initTransformerFactory(factory);
}
static class UserException extends Exception {
private final SAXException exception;
UserException(SAXException exception) {
this.exception = exception;
}
SAXException getException() {
return exception;
}
}
static class UserWrapErrorHandler extends CountingErrorHandler {
UserWrapErrorHandler(ErrorHandler errorHandler) {
super(errorHandler);
}
public void warning(SAXParseException exception)
throws SAXException {
try {
super.warning(exception);
}
catch (SAXException e) {
throw new SAXException(new UserException(e));
}
}
public void error(SAXParseException exception)
throws SAXException {
try {
super.error(exception);
}
catch (SAXException e) {
throw new SAXException(new UserException(e));
}
}
public void fatalError(SAXParseException exception)
throws SAXException {
try {
super.fatalError(exception);
}
catch (SAXException e) {
throw new SAXException(new UserException(e));
}
}
}
static class ErrorFilter extends DelegatingContentHandler {
private final ErrorHandler eh;
private final Localizer localizer;
private Locator locator;
ErrorFilter(ContentHandler delegate, ErrorHandler eh, Localizer localizer) {
super(delegate);
this.eh = eh;
this.localizer = localizer;
}
public void setDocumentLocator(Locator locator) {
this.locator = locator;
super.setDocumentLocator(locator);
}
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws SAXException {
if (namespaceURI.equals(ERROR_URI) && localName.equals("error"))
eh.error(new SAXParseException(localizer.message(atts.getValue("", "message"),
atts.getValue("", "arg")),
locator));
super.startElement(namespaceURI, localName, qName, atts);
}
}
static class LocationFilter extends DelegatingContentHandler implements Locator {
private final String mainSystemId;
private String systemId = null;
private int lineNumber = -1;
private int columnNumber = -1;
private SAXException exception = null;
LocationFilter(ContentHandler delegate, String systemId) {
super(delegate);
this.mainSystemId = systemId;
}
SAXException getException() {
return exception;
}
public void setDocumentLocator(Locator locator) {
}
public void startDocument()
throws SAXException {
getDelegate().setDocumentLocator(this);
super.startDocument();
}
public void startElement(String namespaceURI, String localName,
String qName, Attributes atts)
throws SAXException {
systemId = getLocationAttribute(atts, "system-id");
lineNumber = toInteger(getLocationAttribute(atts, "line-number"));
columnNumber = toInteger(getLocationAttribute(atts, "column-number"));
try {
super.startElement(namespaceURI, localName, qName, atts);
}
catch (SAXException e) {
this.exception = e;
setDelegate(null);
}
systemId = null;
lineNumber = -1;
columnNumber = -1;
}
private static String getLocationAttribute(Attributes atts, String name) {
return atts.getValue(LOCATION_URI, name);
}
private static int toInteger(String value) {
if (value == null)
return -1;
try {
return Integer.parseInt(value);
}
catch (NumberFormatException e) {
return -1;
}
}
public String getPublicId() {
return null;
}
public String getSystemId() {
if (systemId != null && !systemId.equals(""))
return systemId;
return mainSystemId;
}
public int getLineNumber() {
return lineNumber;
}
public int getColumnNumber() {
return columnNumber;
}
}
static class SAXErrorListener implements ErrorListener {
private final ErrorHandler eh;
private final String systemId;
private boolean hadError = false;
SAXErrorListener(ErrorHandler eh, String systemId) {
this.eh = eh;
this.systemId = systemId;
}
boolean getHadError() {
return hadError;
}
public void warning(TransformerException exception)
throws TransformerException {
SAXParseException spe = transform(exception);
try {
eh.warning(spe);
}
catch (SAXException e) {
throw new TransformerException(new UserException(e));
}
}
public void error(TransformerException exception)
throws TransformerException {
hadError = true;
SAXParseException spe = transform(exception);
try {
eh.error(spe);
}
catch (SAXException e) {
throw new TransformerException(new UserException(e));
}
}
public void fatalError(TransformerException exception)
throws TransformerException {
hadError = true;
SAXParseException spe = transform(exception);
try {
eh.fatalError(spe);
}
catch (SAXException e) {
throw new TransformerException(new UserException(e));
}
}
SAXParseException transform(TransformerException exception) throws TransformerException {
Throwable cause = exception.getException();
// Xalan takes it upon itself to catch exceptions and pass them to the ErrorListener.
if (cause instanceof RuntimeException)
throw (RuntimeException)cause;
if (cause instanceof SAXException
|| cause instanceof IncorrectSchemaException
|| cause instanceof IOException)
throw exception;
SourceLocator locator = exception.getLocator();
if (locator == null)
return new SAXParseException(exception.getMessage(), null);
// Xalan sometimes loses the mainSystemId; work around this.
String s = locator.getSystemId();
if (s == null)
s = systemId;
return new SAXParseException(exception.getMessage(),
locator.getPublicId(),
s,
locator.getLineNumber(),
locator.getColumnNumber());
}
}
// Minor problem is that
// Saxon 6.5.2 prints to System.err in TemplatesHandlerImpl.getTemplates().
public Schema createSchema(SAXSource source, PropertyMap properties)
throws IOException, SAXException, IncorrectSchemaException {
ErrorHandler eh = properties.get(ValidateProperty.ERROR_HANDLER);
CountingErrorHandler ceh = new CountingErrorHandler(eh);
InputSource in = source.getInputSource();
String systemId = in.getSystemId();
IfValidHandler ifValidHandler = new IfValidHandler();
ifValidHandler.setErrorHandler(ceh);
try {
SAXTransformerFactory factory = (SAXTransformerFactory)transformerFactoryClass.newInstance();
initTransformerFactory(factory);
TransformerHandler transformerHandler = factory.newTransformerHandler(schematron);
ifValidHandler.setDelegate(transformerHandler);
Transformer transformer = transformerHandler.getTransformer();
String phase = properties.get(SchematronProperty.PHASE);
if (phase != null)
transformer.setParameter("phase", phase);
boolean diagnose = properties.contains(SchematronProperty.DIAGNOSE);
if (diagnose)
transformer.setParameter("diagnose", Boolean.TRUE);
PropertyMapBuilder builder = new PropertyMapBuilder(properties);
builder.put(ValidateProperty.ERROR_HANDLER, ifValidHandler);
Validator validator = schematronSchema.createValidator(builder.toPropertyMap());
ifValidHandler.setValidator(validator.getContentHandler());
XMLReader xr = source.getXMLReader();
if (xr == null)
xr = ResolverFactory.createResolver(properties).createXMLReader();
xr.setContentHandler(ifValidHandler);
xr.setDTDHandler(validator.getDTDHandler()); // not strictly necessary
factory.setErrorListener(new SAXErrorListener(ceh, systemId));
TemplatesHandler templatesHandler = factory.newTemplatesHandler();
templatesHandler.setSystemId(systemId);
LocationFilter stage2 = new LocationFilter(new ErrorFilter(templatesHandler, ceh, localizer), systemId);
transformerHandler.setResult(new SAXResult(stage2));
xr.setErrorHandler(ceh);
xr.parse(in);
SAXException exception = stage2.getException();
if (exception != null)
throw exception;
if (ceh.getHadErrorOrFatalError())
throw new IncorrectSchemaException();
// Getting the templates can cause errors to be generated.
Templates templates = templatesHandler.getTemplates();
if (ceh.getHadErrorOrFatalError())
throw new IncorrectSchemaException();
return new SchemaImpl(templates,
transformerFactoryClass,
properties,
supportedPropertyIds);
}
catch (SAXException e) {
throw cleanupSAXException(e);
}
catch (TransformerConfigurationException e) {
throw new SAXException(localizer.message("unexpected_schema_creation_error"));
}
catch (InstantiationException e) {
throw new SAXException(e);
}
catch (IllegalAccessException e) {
throw new SAXException(e);
}
}
private static String fullResourceName(String name) {
String className = SchemaReaderImpl.class.getName();
return className.substring(0, className.lastIndexOf('.')).replace('.', '/') + "/resources/" + name;
}
private static InputStream getResourceAsStream(String resourceName) {
ClassLoader cl = SchemaReaderImpl.class.getClassLoader();
// XXX see if we should borrow 1.2 code from Service
if (cl == null)
return ClassLoader.getSystemResourceAsStream(resourceName);
else
return cl.getResourceAsStream(resourceName);
}
private static SAXException cleanupSAXException(SAXException saxException) {
if (exceptionHasLocation(saxException))
return saxException;
Exception exception = saxException.getException();
if (exception instanceof SAXException && exception.getMessage() == null)
return cleanupSAXException((SAXException)exception);
if (exception instanceof TransformerException)
return cleanupTransformerException((TransformerException)exception);
return saxException;
}
private static SAXException cleanupTransformerException(TransformerException e) {
String message = e.getMessage();
Throwable cause = e.getException();
SourceLocator transformLoc = e.getLocator();
// a TransformerException created with just a Throwable t argument
// gets a message of t.toString()
if (message != null && cause != null && message.equals(cause.toString()))
message = null;
if (message == null && cause instanceof SAXException && transformLoc == null)
return cleanupSAXException((SAXException)cause);
if (cause instanceof TransformerException && transformLoc == null)
return cleanupTransformerException((TransformerException)cause);
Exception exception = null;
if (cause instanceof Exception)
exception = (Exception)cause;
String publicId = null;
String systemId = null;
int lineNumber = -1;
int columnNumber = -1;
if (transformLoc != null) {
publicId = transformLoc.getPublicId();
systemId = transformLoc.getSystemId();
lineNumber = transformLoc.getLineNumber();
columnNumber = transformLoc.getColumnNumber();
}
if (publicId != null || systemId != null || lineNumber >= 0 || columnNumber >= 0)
return new SAXParseException(message, publicId, systemId, lineNumber, columnNumber, exception);
return new SAXException(message, exception);
}
private static boolean exceptionHasLocation(SAXException saxException) {
if (!(saxException instanceof SAXParseException))
return false;
SAXParseException pe = (SAXParseException)saxException;
return (pe.getPublicId() != null || pe.getSystemId() != null
|| pe.getLineNumber() >= 0 || pe.getColumnNumber() >= 0);
}
}