/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.ode.utils.xsd; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ode.utils.msg.MessageBundle; import org.apache.xerces.dom.DOMInputImpl; import org.apache.xerces.impl.xs.XMLSchemaLoader; import org.apache.xerces.xni.XNIException; import org.apache.xerces.xni.parser.XMLEntityResolver; import org.apache.xerces.xni.parser.XMLErrorHandler; import org.apache.xerces.xni.parser.XMLParseException; import org.apache.xerces.xs.XSModel; import org.w3c.dom.ls.LSInput; import java.io.UnsupportedEncodingException; import java.net.URI; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Various utility methods related to XML Schema processing. */ public class XSUtils { private static final Log __log = LogFactory.getLog(XSUtils.class); private static final XsdMessages __msgs = MessageBundle.getMessages(XsdMessages.class); /** * Recursively "capture" XSD documents starting at the given URI and * using an {@link XMLEntityResolver} to obtain document streams. The * result is a mapping from the XSD URI to a byte array containing the * "captured" document stream. * * @param initialUri URI of the schema * @param resolver {@link XMLEntityResolver} used to obtain XSD document streams * * @return mapping between schema URI and the "captured" schema text (in byte form) */ public static Map<URI, byte[]> captureSchema(String initialUri, XMLEntityResolver resolver) throws XsdException { DOMInputImpl input = new DOMInputImpl(); input.setSystemId(initialUri); Map<URI, byte[]> ret = captureSchema(input, resolver); return ret; } /** * Capture the schemas supplied by the reader. <code>systemURI</code> is * required to resolve any relative uris encountered during the parse. * * @param systemURI Used to resolve relative uris. * @param schemaData the top level schema. * @param resolver entity resolver * * @return */ public static Map<URI, byte[]> captureSchema(URI systemURI, String schemaData, XMLEntityResolver resolver) throws XsdException { if (__log.isDebugEnabled()) __log.debug("captureSchema(URI,Text,...): systemURI=" + systemURI); DOMInputImpl input = new DOMInputImpl(); input.setSystemId(systemURI.toString()); input.setStringData(schemaData); Map<URI, byte[]> ret = captureSchema(input, resolver); // Let's not forget the root schema. try { // TODO don't assume UTF-8 - but which encoding is required? // either we need another parameter or the entire idea of // passing in a String needs to be revised. ret.put(systemURI, schemaData.getBytes("UTF-8")); } catch (UnsupportedEncodingException uenc) { throw new RuntimeException(uenc); } return ret; } private static Map<URI, byte[]> captureSchema(LSInput input, XMLEntityResolver resolver) throws XsdException { if (__log.isDebugEnabled()) __log.debug("captureSchema(LSInput,...): input.systemId=" + input.getSystemId()); Map<URI, byte[]> captured = new HashMap<URI, byte[]>(); if (resolver == null) { resolver = new DefaultXMLEntityResolver(); } CapturingXMLEntityResolver cr = new CapturingXMLEntityResolver(captured, resolver); XMLSchemaLoader schemaLoader = new XMLSchemaLoader(); schemaLoader.setEntityResolver(cr); schemaLoader.setFeature("http://apache.org/xml/features/honour-all-schemaLocations", true); LoggingXmlErrorHandler eh = new LoggingXmlErrorHandler(__log); schemaLoader.setErrorHandler(eh); XSModel model = schemaLoader.load(input); // The following mess is due to XMLSchemaLoaders funkyness in error // reporting: sometimes it throws an exception, sometime it returns // null, sometimes it just prints bs to the screen. if (model == null) { /* * Someone inside Xerces will have eaten this exception, for no good * reason. */ List<XMLParseException> errors = eh.getErrors(); if (errors.size() != 0) { __log.error("captureSchema: XMLParseException(s) in " + input); XsdException ex = null; for (XMLParseException xpe : errors) { ex = new XsdException(ex, xpe.getMessage(), xpe.getLineNumber(), xpe.getColumnNumber(), xpe.getLiteralSystemId()); } throw ex; } if (__log.isDebugEnabled()) __log.debug("captureSchema: NULL model (unknown error) for " + input.getSystemId()); } return captured; } /** * Implementation of {@link LoggingXmlErrorHandler} that outputs messages to * a log. */ static class LoggingXmlErrorHandler implements XMLErrorHandler { private Log _log; private ArrayList<XMLParseException> _errors = new ArrayList<XMLParseException>(); /** * Create a new instance that will output to the specified {@link Log} * instance. * @param log the target log, which much be non-<code>null</code> */ public LoggingXmlErrorHandler(Log log) { assert log != null; _log = log; } public List<XMLParseException> getErrors() { return _errors; } /** * @see XMLErrorHandler#warning(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) */ public void warning(String domain, String key, XMLParseException ex) throws XNIException { if (_log.isDebugEnabled()) _log.debug("XSDErrorHandler.warning: domain=" + domain + ", key=" + key,ex); if (ex != null) { _errors.add(ex); throw ex; } } /** * @see org.apache.xerces.xni.parser.XMLErrorHandler#error(java.lang.String, java.lang.String, org.apache.xerces.xni.parser.XMLParseException) */ public void error(String domain, String key, XMLParseException ex) throws XNIException { if (_log.isDebugEnabled()) _log.debug("XSDErrorHandler.error: domain=" + domain + ", key=" + key,ex); if (ex != null) { _errors.add(ex); throw ex; } // Should not reach here, but just in case... throw new XNIException("Unknown XSD error state; domain=" + domain + ", key=" +key); } public void fatalError(String domain, String key, XMLParseException ex) throws XNIException { if (_log.isDebugEnabled()) _log.debug("XSDErrorHandler.fatal: domain=" + domain + ", key=" + key,ex); if (ex != null) { _errors.add(ex); throw ex; } // Should not reach here, but just in case... throw new XNIException("Unknown XSD error state; domain=" + domain + ", key=" +key); } } }