package org.xmlsh.util; import java.io.IOException; import java.util.ArrayList; import java.util.List; import javax.xml.XMLConstants; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; /** * @author DLEE * * Validate an XML Document against a schema Document must come from a stream */ public class XSDValidator { private String mSchema = null; private List<String> mSchemaList = null ; /* * Features stolen from SourceValidator.java from xerces */ /** Schema full checking feature id (http://apache.org/xml/features/validation/schema-full-checking). */ protected static final String SCHEMA_FULL_CHECKING_FEATURE_ID = "http://apache.org/xml/features/validation/schema-full-checking"; /** Honour all schema locations feature id (http://apache.org/xml/features/honour-all-schemaLocations). */ protected static final String HONOUR_ALL_SCHEMA_LOCATIONS_ID = "http://apache.org/xml/features/honour-all-schemaLocations"; /** Validate schema annotations feature id (http://apache.org/xml/features/validate-annotations) */ protected static final String VALIDATE_ANNOTATIONS_ID = "http://apache.org/xml/features/validate-annotations"; /** Generate synthetic schema annotations feature id (http://apache.org/xml/features/generate-synthetic-annotations). */ protected static final String GENERATE_SYNTHETIC_ANNOTATIONS_ID = "http://apache.org/xml/features/generate-synthetic-annotations"; // property ids /** StAX support for reporting line and column numbers property id (javax.xml.stream.isSupportingLocationCoordinates). */ protected static final String IS_SUPPORTING_LOCATION_COORDINATES = "javax.xml.stream.isSupportingLocationCoordinates"; // default settings /** Default schema language (http://www.w3.org/2001/XMLSchema). */ protected static final String DEFAULT_SCHEMA_LANGUAGE = XMLConstants.W3C_XML_SCHEMA_NS_URI; /** Default validation source. */ protected static final String DEFAULT_VALIDATION_SOURCE = "sax"; /** Default schema full checking support (false). */ protected static final boolean DEFAULT_SCHEMA_FULL_CHECKING = false; /** Default honour all schema locations (false). */ protected static final boolean DEFAULT_HONOUR_ALL_SCHEMA_LOCATIONS = false; /** Default validate schema annotations (false). */ protected static final boolean DEFAULT_VALIDATE_ANNOTATIONS = false; /** Default generate synthetic schema annotations (false). */ protected static final boolean DEFAULT_GENERATE_SYNTHETIC_ANNOTATIONS = false; /** Default memory usage report (false). */ protected static final boolean DEFAULT_MEMORY_USAGE = false; private class ValidatorHandler extends DefaultHandler { private Object mParser; public ValidatorHandler(Object parser) { super(); mParser = parser; } /** * Returns a string describing parse exception details */ private String getParseExceptionInfo(SAXParseException spe) { String systemId = spe.getSystemId(); if (systemId == null) { systemId = "null"; } String info = "source: " + systemId + " Line=" + spe.getLineNumber() + ": " + spe.getMessage(); return info; } // The following methods are standard SAX ErrorHandler methods. // See SAX documentation for more info. @Override public void warning(SAXParseException spe) throws SAXException { String message = "XML Parsing Warning: " + getParseExceptionInfo(spe); throw new SAXException(message); } @Override public void error(SAXParseException spe) throws SAXException { // mLogger.error("XML Parsing Error", spe); String message = "XML Parsing Error: " + getParseExceptionInfo(spe); throw new SAXException(message); } @Override public void fatalError(SAXParseException spe) throws SAXException { // mLogger.fatal("XML Parsing Fatal", spe); String message = "Fatal Error: " + getParseExceptionInfo(spe); throw new SAXException(message); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#resolveEntity(java.lang.String, java.lang.String) */ @Override public InputSource resolveEntity(String publicId, String systemId) throws IOException, SAXException { if( systemId.toLowerCase().endsWith(".dtd")) return new InputSource( new NullInputStream()); else return super.resolveEntity(publicId, systemId); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ /* @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { super.startElement(uri, localName, qName, attributes); // THIS WORKS !!!!!!!!!!!!! PSVIProvider psvi = (PSVIProvider) mParser; ElementPSVI elem = psvi.getElementPSVI(); System.out.println("PSVI for: " + elem.getElementDeclaration().getName() ); } */ } public XSDValidator(String schema) { mSchema = schema; } public XSDValidator(List<String> schemas) { mSchemaList = schemas; } /* * Private method to create a SAXParser; if a schema is supplied, validation * properties are set in the SAXParserFactory, and the parser's schema is * set after creation */ public void validate_breaks_with_dtd(Source source ) throws Exception { String schemaLanguage = DEFAULT_SCHEMA_LANGUAGE; boolean schemaFullChecking = DEFAULT_SCHEMA_FULL_CHECKING; boolean honourAllSchemaLocations = DEFAULT_HONOUR_ALL_SCHEMA_LOCATIONS; boolean validateAnnotations = DEFAULT_VALIDATE_ANNOTATIONS; boolean generateSyntheticAnnotations = DEFAULT_GENERATE_SYNTHETIC_ANNOTATIONS; SchemaFactory f = SchemaFactory.newInstance(schemaLanguage); f.setErrorHandler(new ValidatorHandler(null)); f.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking); f.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations); f.setFeature(VALIDATE_ANNOTATIONS_ID, validateAnnotations); f.setFeature(GENERATE_SYNTHETIC_ANNOTATIONS_ID, generateSyntheticAnnotations); List<Source> sources = new ArrayList<Source>(); if( mSchema != null ) sources.add( new StreamSource(mSchema)); Schema s = f.newSchema(sources.toArray(new Source[sources.size()])); Validator v = s.newValidator(); ValidatorHandler validatorHandler = new ValidatorHandler(v); v.setErrorHandler(validatorHandler); v.setFeature(SCHEMA_FULL_CHECKING_FEATURE_ID, schemaFullChecking); v.setFeature(HONOUR_ALL_SCHEMA_LOCATIONS_ID, honourAllSchemaLocations); v.setFeature(VALIDATE_ANNOTATIONS_ID, validateAnnotations); v.setFeature(GENERATE_SYNTHETIC_ANNOTATIONS_ID, generateSyntheticAnnotations); v.validate( source ); } /* * Private method to create a SAXParser; if a schema is supplied, validation * properties are set in the SAXParserFactory, and the parser's schema is * set after creation */ public void validate(InputSource source ) throws Exception { SAXParserFactory f = SAXParserFactory.newInstance(); // f.setValidating(true); f.setFeature("http://xml.org/sax/features/validation", true); f.setFeature("http://apache.org/xml/features/validation/schema", true); f.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); // f.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammer", false); f.setNamespaceAware(true); f.setValidating(true); SAXParser parser = f.newSAXParser(); parser.setProperty( "http://java.sun.com/xml/jaxp/properties/schemaLanguage", "http://www.w3.org/2001/XMLSchema"); if( mSchema != null ) parser.setProperty( "http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation", mSchema); if( mSchemaList != null ) { StringBuffer sb = new StringBuffer(); for( String s : mSchemaList ){ if( sb.length() > 0 ) sb.append(" "); sb.append( s ); } parser.setProperty( "http://apache.org/xml/properties/schema/external-schemaLocation", sb.toString() ); } parser.parse( source, new ValidatorHandler(parser)); } }