/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package catalog; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.StringReader; import java.io.StringWriter; import java.io.UnsupportedEncodingException; import javax.xml.XMLConstants; import javax.xml.catalog.CatalogFeatures; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLResolver; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.URIResolver; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stax.StAXSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.testng.Assert; import org.testng.annotations.Test; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.ls.LSInput; import org.w3c.dom.ls.LSResourceResolver; import org.xml.sax.Attributes; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.ext.DefaultHandler2; /** * Base class: * Initialized xml/xsd/xsl used for the test; * Handler classes * * @author huizhe.wang@oracle.com */ public class CatalogSupportBase { // the System Property for the USE_CATALOG feature final static String SP_USE_CATALOG = "javax.xml.useCatalog"; boolean debug = false; String filepath; String slash = ""; protected void setUp() { String file1 = getClass().getResource("CatalogSupport.xml").getFile(); if (System.getProperty("os.name").contains("Windows")) { filepath = file1.substring(1, file1.lastIndexOf("/") + 1); slash = "/"; } else { filepath = file1.substring(0, file1.lastIndexOf("/") + 1); } initFiles(); } String xml_catalog, xml_bogus_catalog; // For tests using system.xml String xml_system, dtd_system, dtd_systemResolved; final String elementInSystem = "catalogtest"; final String expectedWCatalog = "Test system entry"; final String expectedWResolver = "Test resolved by an EntityHandler, rather than a Catalog entry"; // For tests using XInclude.xml String xml_xInclude, xml_xIncludeSimple; final String elementInXISimple = "blue"; final String contentInXIutf8 = "trjsagdkasgdhasdgashgdhsadgashdg"; final String contentInUIutf8Catalog = "usingCatalog"; // For the xsd import and include String xsd_xmlSchema, dtd_xmlSchema, dtd_datatypes; String xsd_xmlSchema_import, xsd_xml; String xml_val_test, xml_val_test_id, xsd_val_test; String xsd_include_company, xsd_include_person, xsd_include_product; String xsl_include, xsl_includeDTD, xsl_import_html, xsl_include_header, xsl_include_footer; // For the xsl import and include String xml_xsl, xml_xslDTD; // For document function String xml_doc, xsl_doc, xml_doc2; void initFiles() { xml_system = filepath + "system.xml"; dtd_system = filepath + "system.dtd"; dtd_systemResolved = ""; xml_catalog = filepath + "CatalogSupport.xml"; xml_bogus_catalog = filepath + "CatalogSupport_bogus.xml"; xml_xInclude = "\n" + "\n"; xml_xIncludeSimple = filepath + "XI_simple.xml"; xsd_xmlSchema = "" + "" + "" + " " + " " + " Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp " + " Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp " + " " + " " + ""; dtd_xmlSchema = filepath + "XMLSchema.dtd"; dtd_datatypes = filepath + "datatypes.dtd"; xsd_xmlSchema_import = "" + "" + " " + " " + " Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp " + " Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp " + " " + " " + "" + " " + " " + " " + " Get access to the xml: attribute groups for xml:lang" + " as declared on 'schema' and 'documentation' below" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; xsd_xml = filepath + "xml.xsd"; xsd_include_company = "" + "" + " " + " " + " " + " " + " " + " " + " " + " " + " " + " " + ""; xsd_include_person = filepath + "XSDInclude_person.xsd"; xsd_include_product = filepath + "XSDInclude_product.xsd"; xsl_include = "" + "" + " " + " " + " " + "" + " " + "

" + "
" + " " + " " + "" + "
"; xsl_includeDTD = "" + "" + "" + " " + " " + " " + "" + " " + "

" + "
" + " " + " " + "" + "
"; xsl_import_html = filepath + "XSLImport_html.xsl"; xsl_include_header = filepath + "XSLInclude_header.xsl"; xsl_include_footer = filepath + "XSLInclude_footer.xsl"; xml_val_test = filepath + "/val_test.xml"; xml_val_test_id = "file://" + slash + xml_val_test; xsd_val_test = filepath + "/val_test.xsd"; xml_xsl = "\n" + "\n" + "
This is the header
\n" + " Some content\n" + "
footer
\n" + "
"; xml_xslDTD = "\n" + "" + "\n" + "
This is the header
\n" + " Some content\n" + "
footer
\n" + "
"; xml_doc = filepath + "/DocFunc.xml"; xsl_doc = filepath + "/DocFunc.xsl"; xml_doc2 = filepath + "/DocFunc2.xml"; } /* Verifies the Catalog support on SAXParser. */ public void testSAX(boolean setUseCatalog, boolean useCatalog, String catalog, String xml, MyHandler handler, String expected) throws Exception { SAXParser parser = getSAXParser(setUseCatalog, useCatalog, catalog); parser.parse(xml, handler); assertEquals(expected, handler.getResult().trim(), ""); } /* Verifies the Catalog support on XMLReader. */ public void testXMLReader(boolean setUseCatalog, boolean useCatalog, String catalog, String xml, MyHandler handler, String expected) throws Exception { XMLReader reader = getXMLReader(setUseCatalog, useCatalog, catalog); reader.setContentHandler(handler); reader.parse(xml); assertEquals(expected, handler.getResult().trim(), ""); } /* Verifies the Catalog support on XInclude. */ public void testXInclude(boolean setUseCatalog, boolean useCatalog, String catalog, String xml, MyHandler handler, String expected) throws Exception { SAXParser parser = getSAXParser(setUseCatalog, useCatalog, catalog); parser.parse(new InputSource(new StringReader(xml)), handler); debugPrint("handler.result:" + handler.getResult()); assertEquals(expected, handler.getResult(), "Catalog support for XInclude"); } /* Verifies the Catalog support on DOM parser. */ public void testDOM(boolean setUseCatalog, boolean useCatalog, String catalog, String xml, MyHandler handler, String expected) throws Exception { DocumentBuilder docBuilder = getDomBuilder(setUseCatalog, useCatalog, catalog); docBuilder.setEntityResolver(handler); Document doc = docBuilder.parse(xml); Node node = doc.getElementsByTagName(elementInSystem).item(0); String result = node.getFirstChild().getTextContent(); assertEquals(expected, result.trim(), "Catalog support for DOM"); } /* Verifies the Catalog support on resolving DTD, xsd import and include in Schema files. */ public void testValidation(boolean setUseCatalog, boolean useCatalog, String catalog, String xsd, LSResourceResolver resolver) throws Exception { SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); // use resolver or catalog if resolver = null if (resolver != null) { factory.setResourceResolver(resolver); } if (setUseCatalog) { factory.setFeature(XMLConstants.USE_CATALOG, useCatalog); } factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); Schema schema = factory.newSchema(new StreamSource(new StringReader(xsd))); success("XMLSchema.dtd and datatypes.dtd are resolved."); } /** * Verifies Catalog Support for the Validator. * @param setUseCatalog1 a flag to indicate whether USE_CATALOG shall be set * on the factory. * @param setUseCatalog2 a flag to indicate whether USE_CATALOG shall be set * on the Validator. * @param source the XML source * @param resolver1 a resolver to be set on the factory if specified * @param resolver2 a resolver to be set on the Validator if specified * @param catalog1 a catalog to be set on the factory if specified * @param catalog2 a catalog to be set on the Validator if specified */ public void testValidator(boolean setUseCatalog1, boolean setUseCatalog2, boolean useCatalog, Source source, LSResourceResolver resolver1, LSResourceResolver resolver2, String catalog1, String catalog2) throws Exception { SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); if (setUseCatalog1) { schemaFactory.setFeature(XMLConstants.USE_CATALOG, useCatalog); } if (catalog1 != null) { schemaFactory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog1); } if (resolver1 != null) { schemaFactory.setResourceResolver(resolver1); } Schema schema = schemaFactory.newSchema(); Validator validator = schema.newValidator(); if (setUseCatalog2) { validator.setFeature(XMLConstants.USE_CATALOG, useCatalog); } if (catalog2 != null) { validator.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog2); } if (resolver2 != null) { validator.setResourceResolver(resolver2); } validator.validate(source); } /* Verifies the Catalog support on resolving DTD, xsl import and include in XSL files. */ public void testXSLImport(boolean setUseCatalog, boolean useCatalog, String catalog, SAXSource xsl, StreamSource xml, URIResolver resolver, String expected) throws Exception { TransformerFactory factory = getTransformerFactory(setUseCatalog, useCatalog, catalog, resolver); Transformer transformer = factory.newTransformer(xsl); StringWriter out = new StringWriter(); transformer.transform(xml, new StreamResult(out)); debugPrint("out:\n" + out.toString()); Assert.assertTrue(out.toString().contains(expected), "testXSLImport"); } /* Verifies the Catalog support on resolving DTD, xsl import and include in XSL files. */ public void testXSLImportWTemplates(boolean setUseCatalog, boolean useCatalog, String catalog, SAXSource xsl, StreamSource xml, URIResolver resolver, String expected) throws Exception { TransformerFactory factory = getTransformerFactory(setUseCatalog, useCatalog, catalog, resolver); Transformer transformer = factory.newTemplates(xsl).newTransformer(); StringWriter out = new StringWriter(); transformer.transform(xml, new StreamResult(out)); Assert.assertTrue(out.toString().contains(expected), "testXSLImportWTemplates"); } /** * Returns an instance of SAXParser with a catalog if one is provided. * * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set * through the factory * @param useCatalog the value of USE_CATALOG * @param catalog a catalog * @return an instance of SAXParser * @throws ParserConfigurationException * @throws SAXException */ SAXParser getSAXParser(boolean setUseCatalog, boolean useCatalog, String catalog) throws ParserConfigurationException, SAXException { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); spf.setXIncludeAware(true); if (setUseCatalog) { spf.setFeature(XMLConstants.USE_CATALOG, useCatalog); } SAXParser parser = spf.newSAXParser(); parser.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); return parser; } /** * Returns an instance of XMLReader with a catalog if one is provided. * * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set * through the factory * @param useCatalog the value of USE_CATALOG * @param catalog a catalog * @return an instance of XMLReader * @throws ParserConfigurationException * @throws SAXException */ XMLReader getXMLReader(boolean setUseCatalog, boolean useCatalog, String catalog) throws ParserConfigurationException, SAXException { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setNamespaceAware(true); XMLReader reader = spf.newSAXParser().getXMLReader(); if (setUseCatalog) { reader.setFeature(XMLConstants.USE_CATALOG, useCatalog); } reader.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); return reader; } /** * Returns an instance of DocumentBuilder that may have set a Catalog. * * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set * through the factory * @param useCatalog the value of USE_CATALOG * @param catalog a catalog * @return an instance of DocumentBuilder * @throws ParserConfigurationException */ DocumentBuilder getDomBuilder(boolean setUseCatalog, boolean useCatalog, String catalog) throws ParserConfigurationException { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); if (setUseCatalog) { dbf.setFeature(XMLConstants.USE_CATALOG, useCatalog); } dbf.setAttribute(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); DocumentBuilder docBuilder = dbf.newDocumentBuilder(); return docBuilder; } /** * Creates a DOMSource. * * @param uri the URI to the XML source file * @param systemId the systemId of the source * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set * through the factory * @param useCatalog the value of USE_CATALOG * @param catalog a catalog * @return a DOMSource * @throws Exception */ DOMSource getDOMSource(String uri, String systemId, boolean setUseCatalog, boolean useCatalog, String catalog) { DOMSource ds = null; try { DocumentBuilder builder = getDomBuilder(setUseCatalog, useCatalog, catalog); Document doc = builder.parse(new File(uri)); ds = new DOMSource(doc, systemId); } catch (Exception e) {} return ds; } /** * Creates a StAXSource. * * @param xmlFile the XML source file * @param xmlFileId the systemId of the source * @return a StAXSource * @throws XMLStreamException * @throws FileNotFoundException */ StAXSource getStaxSource(String xmlFile, String xmlFileId) { StAXSource ss = null; try { ss = new StAXSource( XMLInputFactory.newFactory().createXMLEventReader( xmlFileId, new FileInputStream(xmlFile))); } catch (Exception e) {} return ss; } /** * Creates an XMLStreamReader. * @param catalog the path to a catalog * @param xml the xml to be parsed * @param resolver a resolver to be set on the reader * @return an instance of the XMLStreamReader * @throws FileNotFoundException * @throws XMLStreamException */ XMLStreamReader getStreamReader(boolean setUseCatalog, boolean useCatalog, String catalog, String xml, XMLResolver resolver) throws FileNotFoundException, XMLStreamException { XMLInputFactory factory = XMLInputFactory.newInstance(); factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); factory.setProperty(XMLInputFactory.IS_COALESCING, true); factory.setProperty(XMLInputFactory.RESOLVER, resolver); if (setUseCatalog) { factory.setProperty(XMLConstants.USE_CATALOG, useCatalog); } InputStream entityxml = new FileInputStream(xml); XMLStreamReader streamReader = factory.createXMLStreamReader(xml, entityxml); return streamReader; } /** * Returns the text of the first element found by the reader. * @param streamReader the XMLStreamReader * @return the text of the first element * @throws XMLStreamException */ String getText(XMLStreamReader streamReader) throws XMLStreamException { while(streamReader.hasNext()){ int eventType = streamReader.next() ; if(eventType == XMLStreamConstants.START_ELEMENT){ eventType = streamReader.next() ; if(eventType == XMLStreamConstants.CHARACTERS){ return streamReader.getText() ; } } } return null; } /** * Returns an instance of TransformerFactory with either a custom URIResolver * or Catalog. * * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set * through the factory * @param useCatalog the value of USE_CATALOG * @param catalog a catalog * @param resolver a custom resolver * @return an instance of TransformerFactory * @throws Exception */ TransformerFactory getTransformerFactory(boolean setUseCatalog, boolean useCatalog, String catalog, URIResolver resolver) throws Exception { TransformerFactory factory = TransformerFactory.newInstance(); if (setUseCatalog) { factory.setFeature(XMLConstants.USE_CATALOG, useCatalog); } if (catalog != null) { factory.setAttribute(CatalogFeatures.Feature.FILES.getPropertyName(), catalog); } // use resolver or catalog if resolver = null if (resolver != null) { factory.setURIResolver(resolver); } return factory; } void assertNotNull(Object obj, String msg) { if (obj == null) { debugPrint("Test failed: " + msg); } else { debugPrint("Test passed: " + obj + " is not null"); } } void assertEquals(String expected, String actual, String msg) { if (!expected.equals(actual)) { debugPrint("Test failed: " + msg); } else { debugPrint("Test passed: "); } debugPrint("Expected: " + expected); debugPrint("Actual: " + actual); } void fail(String msg) { System.out.println("Test failed:"); System.out.println(msg); } void success(String msg) { System.out.println("Test succeded:"); System.out.println(msg); } void debugPrint(String msg) { if (debug) { System.out.println(msg); } } /** * Extends MyStaxResolver to override resolveEntity */ class MyStaxEntityResolver implements XMLResolver { public MyStaxEntityResolver() { } public Object resolveEntity(String publicId, String systemId, String baseURI, String namespace) throws javax.xml.stream.XMLStreamException { try { return new ByteArrayInputStream( "".getBytes("UTF-8")); } catch (UnsupportedEncodingException ex) { return null; } } } /** * A custom XMLResolver */ class MyStaxResolver implements XMLResolver { public MyStaxResolver() { } public Object resolveEntity(String publicId, String systemId, String baseURI, String namespace) throws javax.xml.stream.XMLStreamException { return null; } } /** * Extends MyHandler and overrides resolveEntity */ class MyEntityHandler extends MyHandler { String[] systemIds; InputSource[] returnValues; public MyEntityHandler(String[] systemIds, InputSource[] returnValues, String elementName) { super(elementName); this.systemIds = systemIds; this.returnValues = returnValues; } @Override public InputSource resolveEntity(String name, String publicId, String baseURI, String systemId) { for (int i = 0; i < systemIds.length; i++) { if (systemId.endsWith(systemIds[i])) { return returnValues[i]; } } return null; } } /** * SAX handler */ public class MyHandler extends DefaultHandler2 implements ErrorHandler { String elementName, currentElementName, result; StringBuilder textContent = new StringBuilder(); /** * * @param elementName the name of the element from which the content * is to be captured */ MyHandler(String elementName) { textContent.setLength(0); this.elementName = elementName; } String getResult() { return result.trim(); } public void startDocument() throws SAXException { } public void endDocument() throws SAXException { } public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { currentElementName = localName; textContent.delete(0, textContent.length()); try { debugPrint("Element: " + uri + ":" + localName + " " + qName); } catch (Exception e) { throw new SAXException(e); } } public void endElement(String uri, String localName, String qName) throws SAXException { debugPrint("Text: " + textContent.toString() + ""); debugPrint("End Element: " + uri + ":" + localName + " " + qName); if (currentElementName.equals(elementName)) { result = textContent.toString(); } } public void characters(char ch[], int start, int length) throws SAXException { if (currentElementName.equals(elementName)) { textContent.append(ch, start, length); } } public void internalEntityDecl(String name, String value) throws SAXException { super.internalEntityDecl(name, value); debugPrint("internalEntityDecl() is invoked for entity : " + name); } public void externalEntityDecl(String name, String publicId, String systemId) throws SAXException { super.externalEntityDecl(name, publicId, systemId); debugPrint("externalEntityDecl() is invoked for entity : " + name); } public void startEntity(String name) throws SAXException { super.startEntity(name); // debugPrint("startEntity() is invoked for entity : " + name) ; } public void endEntity(String name) throws SAXException { super.endEntity(name); // debugPrint("endEntity() is invoked for entity : " + name) ; } public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { debugPrint("resolveEntity(publicId, systemId) is invoked"); return super.resolveEntity(publicId, systemId); } /** * public InputSource resolveEntity(String name, String publicId, String * baseURI, String systemId) throws SAXException, IOException { * System.out.println("resolveEntity(name, publicId, baseURI, systemId) * is invoked"); return super.resolveEntity(name, publicId, baseURI, * systemId); } */ public InputSource getExternalSubset(String name, String baseURI) throws SAXException, IOException { debugPrint("getExternalSubset() is invoked"); return super.getExternalSubset(name, baseURI); } } /** * The purpose of this class, vs an anonymous class, is to show clearly what * we're testing by passing the parameters to the constructor. */ class SourceResolver implements LSResourceResolver { String publicId; String[] systemIds; XmlInput[] returnValues; public SourceResolver(String publicId, String[] systemIds, XmlInput[] returnValues) { this.publicId = publicId; this.systemIds = systemIds; this.returnValues = returnValues; } @Override public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { for (int i = 0; i < systemIds.length; i++) { if (systemId.endsWith(systemIds[i])) { return returnValues[i]; } } return null; } } class XmlInput implements LSInput { private InputStream inputStream; private String systemId; private String baseUri; public XmlInput(InputStream inputStream, String systemId, String baseUri) { this.inputStream = inputStream; this.systemId = systemId; this.baseUri = baseUri; } @Override public Reader getCharacterStream() { return null; } @Override public void setCharacterStream(Reader characterStream) { } @Override public InputStream getByteStream() { return inputStream; } @Override public void setByteStream(InputStream byteStream) { this.inputStream = byteStream; } @Override public String getStringData() { return null; } @Override public void setStringData(String stringData) { } @Override public String getSystemId() { return systemId; } @Override public void setSystemId(String systemId) { this.systemId = systemId; } @Override public String getPublicId() { return null; } @Override public void setPublicId(String publicId) { } @Override public String getBaseURI() { return baseUri; } @Override public void setBaseURI(String baseURI) { this.baseUri = baseURI; } @Override public String getEncoding() { return null; } @Override public void setEncoding(String encoding) { } @Override public boolean getCertifiedText() { return false; } @Override public void setCertifiedText(boolean certifiedText) { } } class XslResolver implements URIResolver { String[] hrefs; Source[] returnValues; public XslResolver(String[] href, Source[] returnValues) { this.hrefs = href; this.returnValues = returnValues; } @Override public Source resolve(String href, String base) throws TransformerException { for (int i = 0; i < hrefs.length; i++) { if (href.endsWith(hrefs[i])) { return returnValues[i]; } } return null; } } }