1 /*
   2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package catalog;
  25 
  26 import static jaxp.library.JAXPTestUtilities.getSystemProperty;
  27 
  28 import java.io.ByteArrayInputStream;
  29 import java.io.File;
  30 import java.io.FileInputStream;
  31 import java.io.FileNotFoundException;
  32 import java.io.IOException;
  33 import java.io.InputStream;
  34 import java.io.Reader;
  35 import java.io.StringReader;
  36 import java.io.StringWriter;
  37 import java.io.UnsupportedEncodingException;
  38 import javax.xml.XMLConstants;
  39 import javax.xml.catalog.CatalogFeatures;
  40 import javax.xml.catalog.CatalogResolver;
  41 import javax.xml.parsers.DocumentBuilder;
  42 import javax.xml.parsers.DocumentBuilderFactory;
  43 import javax.xml.parsers.ParserConfigurationException;
  44 import javax.xml.parsers.SAXParser;
  45 import javax.xml.parsers.SAXParserFactory;
  46 import javax.xml.stream.XMLInputFactory;
  47 import javax.xml.stream.XMLResolver;
  48 import javax.xml.stream.XMLStreamConstants;
  49 import javax.xml.stream.XMLStreamException;
  50 import javax.xml.stream.XMLStreamReader;
  51 import javax.xml.transform.Source;
  52 import javax.xml.transform.Transformer;
  53 import javax.xml.transform.TransformerException;
  54 import javax.xml.transform.TransformerFactory;
  55 import javax.xml.transform.URIResolver;
  56 import javax.xml.transform.dom.DOMSource;
  57 import javax.xml.transform.sax.SAXSource;
  58 import javax.xml.transform.stax.StAXSource;
  59 import javax.xml.transform.stream.StreamResult;
  60 import javax.xml.transform.stream.StreamSource;
  61 import javax.xml.validation.Schema;
  62 import javax.xml.validation.SchemaFactory;
  63 import javax.xml.validation.Validator;
  64 import org.testng.Assert;
  65 import org.w3c.dom.Document;
  66 import org.w3c.dom.Node;
  67 import org.w3c.dom.ls.LSInput;
  68 import org.w3c.dom.ls.LSResourceResolver;
  69 import org.xml.sax.Attributes;
  70 import org.xml.sax.ErrorHandler;
  71 import org.xml.sax.InputSource;
  72 import org.xml.sax.SAXException;
  73 import org.xml.sax.XMLReader;
  74 import org.xml.sax.ext.DefaultHandler2;
  75 
  76 /**
  77  * Base class:
  78  * Initialized xml/xsd/xsl used for the test;
  79  * Handler classes
  80  *
  81  * @author huizhe.wang@oracle.com
  82  */
  83 public class CatalogSupportBase {
  84     // the System Property for the USE_CATALOG feature
  85     final static String SP_USE_CATALOG = "javax.xml.useCatalog";
  86 
  87     boolean debug = false;
  88 
  89     String filepath;
  90     String slash = "";
  91 
  92     protected void setUp() {
  93         String file1 = getClass().getResource("CatalogSupport.xml").getFile();
  94         if (getSystemProperty("os.name").contains("Windows")) {
  95             filepath = file1.substring(1, file1.lastIndexOf("/") + 1);
  96             slash = "/";
  97         } else {
  98             filepath = file1.substring(0, file1.lastIndexOf("/") + 1);
  99         }
 100 
 101         initFiles();
 102     }
 103 
 104     String xml_catalog, xml_bogus_catalog;
 105 
 106     // For tests using system.xml
 107     String xml_system, dtd_system, dtd_systemResolved;
 108     final String elementInSystem = "catalogtest";
 109     final String expectedWCatalog = "Test system entry";
 110     final String expectedWResolver = "Test resolved by an EntityHandler, rather than a Catalog entry";
 111 
 112     // For tests using XInclude.xml
 113     String xml_xInclude, xml_xIncludeSimple;
 114     final String elementInXISimple = "blue";
 115     final String contentInXIutf8 = "trjsagdkasgdhasdgashgdhsadgashdg";
 116     final String contentInUIutf8Catalog = "usingCatalog";
 117 
 118     // For the xsd import and include
 119     String xsd_xmlSchema, dtd_xmlSchema, dtd_datatypes;
 120     String xsd_xmlSchema_import, xsd_xml;
 121     String xml_val_test, xml_val_test_id, xsd_val_test;
 122     String xsd_include_company, xsd_include_person, xsd_include_product;
 123     String xsl_include, xsl_includeDTD, xsl_import_html, xsl_include_header, xsl_include_footer;
 124 
 125     // For the xsl import and include
 126     String xml_xsl, xml_xslDTD;
 127 
 128     // For document function
 129     String xml_doc, xsl_doc, xml_doc2;
 130 
 131     void initFiles() {
 132         xml_system = filepath + "system.xml";
 133         dtd_system = filepath + "system.dtd";
 134         dtd_systemResolved = "<!ENTITY system \"resolved by an EntityHandler, rather than a Catalog\">";
 135 
 136         xml_catalog = filepath + "CatalogSupport.xml";
 137         xml_bogus_catalog = filepath + "CatalogSupport_bogus.xml";
 138 
 139         xml_xInclude = "<?xml version=\"1.0\"?>\n" +
 140             "<xinclude:include xmlns:xinclude=\"http://www.w3.org/2001/XInclude\"\n" +
 141             "  href=\"XI_simple.xml\"/>\n";
 142         xml_xIncludeSimple = filepath + "XI_simple.xml";
 143 
 144         xsd_xmlSchema = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>"
 145                 + "<!DOCTYPE xs:schema PUBLIC \"-//W3C//DTD XMLSCHEMA 200102//EN\" \"pathto/XMLSchema.dtd\" >"
 146                 + "<xs:schema targetNamespace=\"http://www.w3.org/2001/XMLSchema\" blockDefault=\"#all\" "
 147                 + "           elementFormDefault=\"qualified\" version=\"1.0\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" "
 148                 + "           xml:lang=\"EN\" xmlns:hfp=\"http://www.w3.org/2001/XMLSchema-hasFacetAndProperty\">"
 149                 + " <xs:annotation>"
 150                 + "  <xs:documentation>"
 151                 + "    Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp "
 152                 + "    Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp "
 153                 + "  </xs:documentation>"
 154                 + " </xs:annotation>"
 155                 + "</xs:schema>";
 156         dtd_xmlSchema = filepath + "XMLSchema.dtd";
 157         dtd_datatypes = filepath + "datatypes.dtd";
 158 
 159         xsd_xmlSchema_import = "<?xml version=\"1.1\" encoding=\"UTF-8\"?>"
 160                 + "<xs:schema targetNamespace=\"http://www.w3.org/2001/XMLSchema\" "
 161                 + "blockDefault=\"#all\" elementFormDefault=\"qualified\" version=\"1.0\" "
 162                 + "xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xml:lang=\"EN\" "
 163                 + "xmlns:hfp=\"http://www.w3.org/2001/XMLSchema-hasFacetAndProperty\">"
 164                 + " <xs:annotation>"
 165                 + "  <xs:documentation>"
 166                 + "    Part 1 version: Id: structures.xsd,v 1.2 2004/01/15 11:34:25 ht Exp "
 167                 + "    Part 2 version: Id: datatypes.xsd,v 1.3 2004/01/23 18:11:13 ht Exp "
 168                 + "  </xs:documentation>"
 169                 + " </xs:annotation>"
 170                 + ""
 171                 + " <xs:import namespace=\"http://www.w3.org/XML/1998/namespace\" "
 172                 + "schemaLocation=\"http://www.w3.org/2001/pathto/xml.xsd\">"
 173                 + "   <xs:annotation>"
 174                 + "     <xs:documentation>"
 175                 + "       Get access to the xml: attribute groups for xml:lang"
 176                 + "       as declared on 'schema' and 'documentation' below"
 177                 + "     </xs:documentation>"
 178                 + "   </xs:annotation>"
 179                 + " </xs:import>"
 180                 + " <xs:element name=\"schema\" id=\"schema\">"
 181                 + "  <xs:complexType>"
 182                 + "   <xs:simpleContent>"
 183                 + "    <xs:extension base=\"xs:integer\">"
 184                 + "     <xs:attribute ref=\"xml:lang\"/>"
 185                 + "    </xs:extension>"
 186                 + "   </xs:simpleContent>"
 187                 + "  </xs:complexType>"
 188                 + " </xs:element>"
 189                 + "</xs:schema>";
 190 
 191         xsd_xml = filepath + "xml.xsd";
 192 
 193         xsd_include_company = "<?xml version=\"1.1\"?>"
 194                 + "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
 195                 + "            targetNamespace=\"http://www.company.org\""
 196                 + "            xmlns=\"http://www.company.org\""
 197                 + "            elementFormDefault=\"qualified\">"
 198                 + "    <xsd:include schemaLocation=\"pathto/XSDInclude_person.xsd\"/>"
 199                 + "    <xsd:include schemaLocation=\"pathto/XSDInclude_product.xsd\"/>"
 200                 + "    <xsd:element name=\"Company\">"
 201                 + "        <xsd:complexType>"
 202                 + "            <xsd:sequence>"
 203                 + "                <xsd:element name=\"Person\" type=\"PersonType\""
 204                 + "                             maxOccurs=\"unbounded\"/>"
 205                 + "                <xsd:element name=\"Product\" type=\"ProductType\""
 206                 + "                             maxOccurs=\"unbounded\"/>"
 207                 + "            </xsd:sequence>"
 208                 + "        </xsd:complexType>"
 209                 + "    </xsd:element>"
 210                 + "</xsd:schema>";
 211         xsd_include_person = filepath + "XSDInclude_person.xsd";
 212         xsd_include_product = filepath + "XSDInclude_product.xsd";
 213 
 214         xsl_include = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
 215                 + "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">"
 216                 + "  <xsl:import href=\"pathto/XSLImport_html.xsl\"/>"
 217                 + "  <xsl:include href=\"pathto/XSLInclude_header.xsl\"/>"
 218                 + "  "
 219                 + ""
 220                 + "  <xsl:template match=\"content/title\">"
 221                 + "   <h1><xsl:apply-templates/></h1>"
 222                 + "  </xsl:template>"
 223                 + "  "
 224                 + "  <xsl:include href=\"pathto/XSLInclude_footer.xsl\"/>"
 225                 + ""
 226                 + "</xsl:stylesheet>";
 227         xsl_includeDTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
 228                 + "<!DOCTYPE HTMLlat1 SYSTEM \"http://openjdk.java.net/xml/catalog/dtd/XSLDTD.dtd\">"
 229                 + "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\" version=\"1.0\">"
 230                 + "  <xsl:import href=\"pathto/XSLImport_html.xsl\"/>"
 231                 + "  <xsl:include href=\"pathto/XSLInclude_header.xsl\"/>"
 232                 + "  "
 233                 + ""
 234                 + "  <xsl:template match=\"content/title\">"
 235                 + "   <h1><xsl:apply-templates/></h1>"
 236                 + "  </xsl:template>"
 237                 + "  "
 238                 + "  <xsl:include href=\"pathto/XSLInclude_footer.xsl\"/>"
 239                 + ""
 240                 + "</xsl:stylesheet>";
 241 
 242         xsl_import_html = filepath + "XSLImport_html.xsl";
 243         xsl_include_header = filepath + "XSLInclude_header.xsl";
 244         xsl_include_footer = filepath + "XSLInclude_footer.xsl";
 245 
 246         xml_val_test = filepath + "/val_test.xml";
 247         xml_val_test_id = "file://" + slash + xml_val_test;
 248         xsd_val_test = filepath + "/val_test.xsd";
 249 
 250         xml_xsl = "<?xml version=\"1.0\"?>\n" +
 251                 "<content>\n" +
 252                 "    <header>This is the header</header>\n" +
 253                 "    Some content\n" +
 254                 "    <footer>footer</footer>\n" +
 255                 "</content>";
 256 
 257         xml_xslDTD = "<?xml version=\"1.0\"?>\n" +
 258                 "<!DOCTYPE content SYSTEM \"http://openjdk.java.net/xml/catalog/dtd/include.dtd\">" +
 259                 "<content>\n" +
 260                 "    <header>This is the header</header>\n" +
 261                 "    Some content\n" +
 262                 "    <footer>footer</footer>\n" +
 263                 "</content>";
 264 
 265         xml_doc = filepath + "/DocFunc.xml";
 266         xsl_doc = filepath + "/DocFunc.xsl";
 267         xml_doc2 = filepath + "/DocFunc2.xml";
 268     }
 269 
 270 
 271     /*
 272        Verifies the Catalog support on SAXParser.
 273     */
 274     public void testSAX(boolean setUseCatalog, boolean useCatalog, String catalog,
 275             String xml, MyHandler handler, String expected) throws Exception {
 276         SAXParser parser = getSAXParser(setUseCatalog, useCatalog, catalog);
 277 
 278         parser.parse(xml, handler);
 279         assertEquals(expected, handler.getResult().trim(), "");
 280     }
 281 
 282     /*
 283        Verifies the Catalog support on XMLReader.
 284     */
 285     public void testXMLReader(boolean setUseCatalog, boolean useCatalog, String catalog,
 286             String xml, MyHandler handler, String expected) throws Exception {
 287         XMLReader reader = getXMLReader(setUseCatalog, useCatalog, catalog);
 288 
 289         reader.setContentHandler(handler);
 290         reader.parse(xml);
 291         assertEquals(expected, handler.getResult().trim(), "");
 292     }
 293 
 294     /*
 295        Verifies the Catalog support on XInclude.
 296     */
 297     public void testXInclude(boolean setUseCatalog, boolean useCatalog, String catalog,
 298             String xml, MyHandler handler, String expected) throws Exception {
 299         SAXParser parser = getSAXParser(setUseCatalog, useCatalog, catalog);
 300 
 301         parser.parse(new InputSource(new StringReader(xml)), handler);
 302         debugPrint("handler.result:" + handler.getResult());
 303         assertEquals(expected, handler.getResult(), "Catalog support for XInclude");
 304     }
 305 
 306     /*
 307        Verifies the Catalog support on DOM parser.
 308     */
 309     public void testDOM(boolean setUseCatalog, boolean useCatalog, String catalog,
 310             String xml, MyHandler handler, String expected) throws Exception {
 311         DocumentBuilder docBuilder = getDomBuilder(setUseCatalog, useCatalog, catalog);
 312         docBuilder.setEntityResolver(handler);
 313         Document doc = docBuilder.parse(xml);
 314 
 315         Node node = doc.getElementsByTagName(elementInSystem).item(0);
 316         String result = node.getFirstChild().getTextContent();
 317 
 318         assertEquals(expected, result.trim(), "Catalog support for DOM");
 319     }
 320 
 321     /*
 322        Verifies the Catalog support on StAX parser.
 323     */
 324     public void testStAX(boolean setUseCatalog, boolean useCatalog, String catalog,
 325             String xml, XMLResolver resolver, String expected) throws Exception {
 326 
 327             XMLStreamReader streamReader = getStreamReader(
 328                     setUseCatalog, useCatalog, catalog, xml, resolver);
 329             String text = getText(streamReader, XMLStreamConstants.CHARACTERS);
 330             assertEquals(expected, text.trim(), "Catalog support for StAX");
 331     }
 332 
 333     /*
 334        Verifies that the Catalog support for StAX parser is disabled when
 335        USE_CATALOG == false.
 336     */
 337     public void testStAXNegative(boolean setUseCatalog, boolean useCatalog, String catalog,
 338             String xml, XMLResolver resolver, String expected) throws Exception {
 339 
 340             XMLStreamReader streamReader = getStreamReader(
 341                     setUseCatalog, useCatalog, catalog, xml, resolver);
 342             String text = getText(streamReader, XMLStreamConstants.ENTITY_REFERENCE);
 343             assertEquals(expected, text.trim(), "Catalog support for StAX");
 344     }
 345 
 346     /*
 347        Verifies the Catalog support on resolving DTD, xsd import and include in
 348     Schema files.
 349     */
 350     public void testValidation(boolean setUseCatalog, boolean useCatalog, String catalog,
 351             String xsd, LSResourceResolver resolver)
 352             throws Exception {
 353 
 354         SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 355 
 356         // use resolver or catalog if resolver = null
 357         if (resolver != null) {
 358             factory.setResourceResolver(resolver);
 359         }
 360         if (setUseCatalog) {
 361             factory.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 362         }
 363         factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 364 
 365         Schema schema = factory.newSchema(new StreamSource(new StringReader(xsd)));
 366         success("XMLSchema.dtd and datatypes.dtd are resolved.");
 367     }
 368 
 369     /**
 370      * Verifies Catalog Support for the Validator.
 371      * @param setUseCatalog1 a flag to indicate whether USE_CATALOG shall be set
 372      * on the factory.
 373      * @param setUseCatalog2 a flag to indicate whether USE_CATALOG shall be set
 374      * on the Validator.
 375      * @param source  the XML source
 376      * @param resolver1 a resolver to be set on the factory if specified
 377      * @param resolver2 a resolver to be set on the Validator if specified
 378      * @param catalog1 a catalog to be set on the factory if specified
 379      * @param catalog2 a catalog to be set on the Validator if specified
 380      */
 381     public void testValidator(boolean setUseCatalog1, boolean setUseCatalog2, boolean useCatalog,
 382             Source source, LSResourceResolver resolver1, LSResourceResolver resolver2,
 383             String catalog1, String catalog2)
 384             throws Exception {
 385 
 386             SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
 387             if (setUseCatalog1) {
 388                 schemaFactory.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 389             }
 390             if (catalog1 != null) {
 391                 schemaFactory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog1);
 392             }
 393             if (resolver1 != null) {
 394                 schemaFactory.setResourceResolver(resolver1);
 395             }
 396 
 397             Schema schema = schemaFactory.newSchema();
 398             Validator validator = schema.newValidator();
 399             if (setUseCatalog2) {
 400                 validator.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 401             }
 402             if (catalog2 != null) {
 403                 validator.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog2);
 404             }
 405             if (resolver2 != null) {
 406                 validator.setResourceResolver(resolver2);
 407             }
 408             validator.validate(source);
 409     }
 410 
 411     /*
 412        Verifies the Catalog support on resolving DTD, xsl import and include in
 413     XSL files.
 414     */
 415     public void testXSLImport(boolean setUseCatalog, boolean useCatalog, String catalog,
 416             SAXSource xsl, StreamSource xml,
 417         URIResolver resolver, String expected) throws Exception {
 418 
 419         TransformerFactory factory = getTransformerFactory(setUseCatalog, useCatalog, catalog, resolver);
 420         Transformer transformer = factory.newTransformer(xsl);
 421 
 422         StringWriter out = new StringWriter();
 423         transformer.transform(xml, new StreamResult(out));
 424         debugPrint("out:\n" + out.toString());
 425         Assert.assertTrue(out.toString().contains(expected), "testXSLImport");
 426     }
 427 
 428     /*
 429        Verifies the Catalog support on resolving DTD, xsl import and include in
 430     XSL files.
 431     */
 432     public void testXSLImportWTemplates(boolean setUseCatalog, boolean useCatalog,
 433             String catalog, SAXSource xsl, StreamSource xml,
 434         URIResolver resolver, String expected) throws Exception {
 435 
 436         TransformerFactory factory = getTransformerFactory(setUseCatalog, useCatalog, catalog, resolver);
 437         Transformer transformer = factory.newTemplates(xsl).newTransformer();
 438         StringWriter out = new StringWriter();
 439         transformer.transform(xml, new StreamResult(out));
 440         Assert.assertTrue(out.toString().contains(expected), "testXSLImportWTemplates");
 441     }
 442 
 443     /**
 444      * Returns an instance of SAXParser with a catalog if one is provided.
 445      *
 446      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 447      * through the factory
 448      * @param useCatalog the value of USE_CATALOG
 449      * @param catalog a catalog
 450      * @return an instance of SAXParser
 451      * @throws ParserConfigurationException
 452      * @throws SAXException
 453      */
 454     SAXParser getSAXParser(boolean setUseCatalog, boolean useCatalog, String catalog)
 455             throws ParserConfigurationException, SAXException {
 456         SAXParserFactory spf = SAXParserFactory.newInstance();
 457         spf.setNamespaceAware(true);
 458         spf.setXIncludeAware(true);
 459         if (setUseCatalog) {
 460             spf.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 461         }
 462 
 463         SAXParser parser = spf.newSAXParser();
 464         parser.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 465         return parser;
 466     }
 467 
 468     /**
 469      * Returns an instance of XMLReader with a catalog if one is provided.
 470      *
 471      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 472      * through the factory
 473      * @param useCatalog the value of USE_CATALOG
 474      * @param catalog a catalog
 475      * @return an instance of XMLReader
 476      * @throws ParserConfigurationException
 477      * @throws SAXException
 478      */
 479     XMLReader getXMLReader(boolean setUseCatalog, boolean useCatalog, String catalog)
 480             throws ParserConfigurationException, SAXException {
 481         SAXParserFactory spf = SAXParserFactory.newInstance();
 482         spf.setNamespaceAware(true);
 483         XMLReader reader = spf.newSAXParser().getXMLReader();
 484         if (setUseCatalog) {
 485             reader.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 486         }
 487         reader.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 488         return reader;
 489     }
 490 
 491     /**
 492      * Returns an instance of DocumentBuilder that may have set a Catalog.
 493      *
 494      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 495      * through the factory
 496      * @param useCatalog the value of USE_CATALOG
 497      * @param catalog a catalog
 498      * @return an instance of DocumentBuilder
 499      * @throws ParserConfigurationException
 500      */
 501     DocumentBuilder getDomBuilder(boolean setUseCatalog, boolean useCatalog, String catalog)
 502             throws ParserConfigurationException {
 503         DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 504         dbf.setNamespaceAware(true);
 505         if (setUseCatalog) {
 506             dbf.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 507         }
 508         dbf.setAttribute(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 509         DocumentBuilder docBuilder = dbf.newDocumentBuilder();
 510         return docBuilder;
 511     }
 512 
 513     /**
 514      * Creates a DOMSource.
 515      *
 516      * @param uri the URI to the XML source file
 517      * @param systemId the systemId of the source
 518      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 519      * through the factory
 520      * @param useCatalog the value of USE_CATALOG
 521      * @param catalog a catalog
 522      * @return a DOMSource
 523      * @throws Exception
 524      */
 525     DOMSource getDOMSource(String uri, String systemId, boolean setUseCatalog,
 526             boolean useCatalog, String catalog) {
 527         DOMSource ds = null;
 528         try {
 529             DocumentBuilder builder = getDomBuilder(setUseCatalog, useCatalog, catalog);
 530             Document doc = builder.parse(new File(uri));
 531             ds = new DOMSource(doc, systemId);
 532         } catch (Exception e) {}
 533 
 534         return ds;
 535     }
 536 
 537     /**
 538      * Creates a StAXSource.
 539      *
 540      * @param xmlFile the XML source file
 541      * @param xmlFileId the systemId of the source
 542      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 543      * through the factory
 544      * @param useCatalog the value of USE_CATALOG
 545      * @param catalog a catalog
 546      * @return a StAXSource
 547      * @throws XMLStreamException
 548      * @throws FileNotFoundException
 549      */
 550     StAXSource getStaxSource(String xmlFile, String xmlFileId, boolean setUseCatalog,
 551             boolean useCatalog, String catalog) {
 552         StAXSource ss = null;
 553         try {
 554             XMLInputFactory xif = XMLInputFactory.newFactory();
 555             if (setUseCatalog) {
 556                 xif.setProperty(XMLConstants.USE_CATALOG, useCatalog);
 557             }
 558             xif.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 559             ss = new StAXSource(xif.createXMLEventReader(
 560                         xmlFileId, new FileInputStream(xmlFile)));
 561         } catch (Exception e) {}
 562 
 563         return ss;
 564     }
 565 
 566     /**
 567      * Creates an XMLStreamReader.
 568      *
 569      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 570      * through the factory
 571      * @param useCatalog the value of USE_CATALOG
 572      * @param catalog the path to a catalog
 573      * @param xml the xml to be parsed
 574      * @param resolver a resolver to be set on the reader
 575      * @return an instance of the XMLStreamReader
 576      * @throws FileNotFoundException
 577      * @throws XMLStreamException
 578      */
 579     XMLStreamReader getStreamReader(boolean setUseCatalog, boolean useCatalog,
 580             String catalog, String xml, XMLResolver resolver)
 581             throws FileNotFoundException, XMLStreamException {
 582         XMLInputFactory factory = XMLInputFactory.newInstance();
 583         if (catalog != null) {
 584             factory.setProperty(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 585         }
 586 
 587         factory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true);
 588         factory.setProperty(XMLInputFactory.IS_COALESCING, true);
 589 
 590         if (resolver != null) {
 591             factory.setProperty(XMLInputFactory.RESOLVER, resolver);
 592         }
 593 
 594         if (setUseCatalog) {
 595             factory.setProperty(XMLConstants.USE_CATALOG, useCatalog);
 596         }
 597 
 598         InputStream entityxml = new FileInputStream(xml);
 599         XMLStreamReader streamReader = factory.createXMLStreamReader(xml, entityxml);
 600         return streamReader;
 601     }
 602 
 603     /**
 604      * Returns the text of the first element found by the reader.
 605      * @param streamReader the XMLStreamReader
 606      * @return the text of the first element
 607      * @throws XMLStreamException
 608      */
 609     String getText(XMLStreamReader streamReader, int type) throws XMLStreamException {
 610         StringBuilder text = new StringBuilder();
 611         StringBuilder entityRef = new StringBuilder();
 612 
 613         while(streamReader.hasNext()){
 614             int eventType = streamReader.next();
 615             switch (eventType) {
 616                 case XMLStreamConstants.START_ELEMENT:
 617                     break;
 618                 case XMLStreamConstants.CHARACTERS:
 619                     text.append(streamReader.getText());
 620                     break;
 621                 case XMLStreamConstants.ENTITY_REFERENCE:
 622                     entityRef.append(streamReader.getText());
 623                     break;
 624             }
 625         }
 626         if (type == XMLStreamConstants.CHARACTERS) {
 627             return text.toString();
 628         } else {
 629             return entityRef.toString();
 630         }
 631     }
 632 
 633     /**
 634      * Returns an instance of TransformerFactory with either a custom URIResolver
 635      * or Catalog.
 636      *
 637      * @param setUseCatalog a flag indicates whether USE_CATALOG shall be set
 638      * through the factory
 639      * @param useCatalog the value of USE_CATALOG
 640      * @param catalog a catalog
 641      * @param resolver a custom resolver
 642      * @return an instance of TransformerFactory
 643      * @throws Exception
 644      */
 645     TransformerFactory getTransformerFactory(boolean setUseCatalog, boolean useCatalog,
 646             String catalog, URIResolver resolver)
 647             throws Exception {
 648 
 649         TransformerFactory factory = TransformerFactory.newInstance();
 650         if (setUseCatalog) {
 651             factory.setFeature(XMLConstants.USE_CATALOG, useCatalog);
 652         }
 653         if (catalog != null) {
 654             factory.setAttribute(CatalogFeatures.Feature.FILES.getPropertyName(), catalog);
 655         }
 656 
 657         // use resolver or catalog if resolver = null
 658         if (resolver != null) {
 659             factory.setURIResolver(resolver);
 660         }
 661 
 662         return factory;
 663     }
 664 
 665     void assertNotNull(Object obj, String msg) {
 666         if (obj == null) {
 667             debugPrint("Test failed: " + msg);
 668         } else {
 669             debugPrint("Test passed: " + obj + " is not null");
 670         }
 671     }
 672 
 673     void assertEquals(String expected, String actual, String msg) {
 674         if (!expected.equals(actual)) {
 675             debugPrint("Test failed: " + msg);
 676         } else {
 677             debugPrint("Test passed: ");
 678         }
 679         debugPrint("Expected: " + expected);
 680         debugPrint("Actual: " + actual);
 681     }
 682 
 683     void fail(String msg) {
 684         System.out.println("Test failed:");
 685         System.out.println(msg);
 686     }
 687 
 688     void success(String msg) {
 689         System.out.println("Test succeded:");
 690         System.out.println(msg);
 691     }
 692 
 693     void debugPrint(String msg) {
 694         if (debug) {
 695             System.out.println(msg);
 696         }
 697     }
 698 
 699     /**
 700      * Extends MyStaxResolver to override resolveEntity
 701      */
 702     class MyStaxEntityResolver implements XMLResolver {
 703 
 704         public MyStaxEntityResolver() {
 705 
 706         }
 707 
 708         public Object resolveEntity(String publicId, String systemId, String baseURI,
 709                 String namespace)
 710                 throws javax.xml.stream.XMLStreamException {
 711             try {
 712                 return new ByteArrayInputStream(
 713                         "<!ENTITY system \"resolved by an EntityHandler, rather than a Catalog\">".getBytes("UTF-8"));
 714             } catch (UnsupportedEncodingException ex) {
 715                 return null;
 716             }
 717         }
 718 
 719     }
 720 
 721     /**
 722      * A custom XMLResolver
 723      */
 724     class MyStaxResolver implements XMLResolver {
 725 
 726         public MyStaxResolver() {
 727         }
 728 
 729         public Object resolveEntity(String publicId, String systemId, String baseURI,
 730                 String namespace) throws javax.xml.stream.XMLStreamException {
 731             return null;
 732         }
 733 
 734     }
 735 
 736 
 737     /**
 738      * Extends MyHandler and overrides resolveEntity with a CatalogResolver
 739      */
 740     class MyCatalogHandler extends MyHandler {
 741         CatalogResolver cr;
 742 
 743         public MyCatalogHandler(CatalogResolver cr, String elementName) {
 744             super(elementName);
 745             this.cr = cr;
 746         }
 747 
 748         @Override
 749         public InputSource resolveEntity(String publicId, String systemId) {
 750             return cr.resolveEntity(publicId, systemId);
 751         }
 752         @Override
 753         public InputSource resolveEntity(String name, String publicId,
 754                 String baseURI, String systemId) {
 755             return cr.resolveEntity(publicId, systemId);
 756         }
 757     }
 758 
 759     /**
 760      * Extends MyHandler and overrides resolveEntity
 761      */
 762     class MyEntityHandler extends MyHandler {
 763         String[] systemIds;
 764         InputSource[] returnValues;
 765         public MyEntityHandler(String[] systemIds, InputSource[] returnValues, String elementName) {
 766             super(elementName);
 767             this.systemIds = systemIds;
 768             this.returnValues = returnValues;
 769         }
 770 
 771         @Override
 772         public InputSource resolveEntity(String name, String publicId,
 773                 String baseURI, String systemId) {
 774             for (int i = 0; i < systemIds.length; i++) {
 775                 if (systemId.endsWith(systemIds[i])) {
 776                     return returnValues[i];
 777                 }
 778             }
 779 
 780             return null;
 781         }
 782     }
 783 
 784     /**
 785      * SAX handler
 786      */
 787     public class MyHandler extends DefaultHandler2 implements ErrorHandler {
 788 
 789         String elementName, currentElementName, result;
 790         StringBuilder textContent = new StringBuilder();
 791 
 792         /**
 793          *
 794          * @param elementName the name of the element from which the content
 795          * is to be captured
 796          */
 797         MyHandler(String elementName) {
 798             textContent.setLength(0);
 799             this.elementName = elementName;
 800         }
 801 
 802         String getResult() {
 803             return result.trim();
 804         }
 805 
 806         public void startDocument() throws SAXException {
 807         }
 808 
 809         public void endDocument() throws SAXException {
 810         }
 811 
 812         public void startElement(String uri, String localName, String qName, Attributes attributes)
 813                 throws SAXException {
 814             currentElementName = localName;
 815             textContent.delete(0, textContent.length());
 816             try {
 817                 debugPrint("Element: " + uri + ":" + localName + " " + qName);
 818             } catch (Exception e) {
 819                 throw new SAXException(e);
 820             }
 821 
 822         }
 823 
 824         public void endElement(String uri, String localName, String qName) throws SAXException {
 825             debugPrint("Text: " + textContent.toString() + "");
 826             debugPrint("End Element: " + uri + ":" + localName + " " + qName);
 827             if (currentElementName.equals(elementName)) {
 828                 result = textContent.toString();
 829             }
 830         }
 831 
 832         public void characters(char ch[], int start, int length) throws SAXException {
 833             if (currentElementName.equals(elementName)) {
 834                 textContent.append(ch, start, length);
 835             }
 836         }
 837 
 838         public void internalEntityDecl(String name, String value) throws SAXException {
 839             super.internalEntityDecl(name, value);
 840             debugPrint("internalEntityDecl() is invoked for entity : " + name);
 841         }
 842 
 843         public void externalEntityDecl(String name, String publicId, String systemId)
 844                 throws SAXException {
 845             super.externalEntityDecl(name, publicId, systemId);
 846             debugPrint("externalEntityDecl() is invoked for entity : " + name);
 847         }
 848 
 849         public void startEntity(String name) throws SAXException {
 850             super.startEntity(name);
 851 //              debugPrint("startEntity() is invoked for entity : " + name) ;
 852         }
 853 
 854         public void endEntity(String name) throws SAXException {
 855             super.endEntity(name);
 856 //              debugPrint("endEntity() is invoked for entity : " + name) ;
 857         }
 858 
 859         public InputSource resolveEntity(String publicId, String systemId)
 860                 throws SAXException, IOException {
 861             debugPrint("resolveEntity(publicId, systemId) is invoked");
 862             return super.resolveEntity(publicId, systemId);
 863         }
 864 
 865         /**
 866          * public InputSource resolveEntity(String name, String publicId, String
 867          * baseURI, String systemId) throws SAXException, IOException {
 868          * System.out.println("resolveEntity(name, publicId, baseURI, systemId)
 869          * is invoked"); return super.resolveEntity(name, publicId, baseURI,
 870          * systemId); }
 871          */
 872         public InputSource getExternalSubset(String name, String baseURI)
 873                 throws SAXException, IOException {
 874             debugPrint("getExternalSubset() is invoked");
 875             return super.getExternalSubset(name, baseURI);
 876         }
 877     }
 878 
 879     /**
 880      * The purpose of this class, vs an anonymous class, is to show clearly what
 881      * we're testing by passing the parameters to the constructor.
 882      */
 883     class SourceResolver implements LSResourceResolver {
 884 
 885         String publicId;
 886         String[] systemIds;
 887         XmlInput[] returnValues;
 888 
 889         public SourceResolver(String publicId, String[] systemIds, XmlInput[] returnValues) {
 890             this.publicId = publicId;
 891             this.systemIds = systemIds;
 892             this.returnValues = returnValues;
 893         }
 894 
 895         @Override
 896         public LSInput resolveResource(String type, String namespaceURI, String publicId,
 897                 String systemId, String baseURI) {
 898             for (int i = 0; i < systemIds.length; i++) {
 899                 if (systemId.endsWith(systemIds[i])) {
 900                     return returnValues[i];
 901                 }
 902             }
 903 
 904             return null;
 905         }
 906     }
 907 
 908     class XmlInput implements LSInput {
 909 
 910         private InputStream inputStream;
 911         private String systemId;
 912         private String baseUri;
 913 
 914         public XmlInput(InputStream inputStream, String systemId, String baseUri) {
 915             this.inputStream = inputStream;
 916             this.systemId = systemId;
 917             this.baseUri = baseUri;
 918         }
 919 
 920         @Override
 921         public Reader getCharacterStream() {
 922             return null;
 923         }
 924 
 925         @Override
 926         public void setCharacterStream(Reader characterStream) {
 927         }
 928 
 929         @Override
 930         public InputStream getByteStream() {
 931             return inputStream;
 932         }
 933 
 934         @Override
 935         public void setByteStream(InputStream byteStream) {
 936             this.inputStream = byteStream;
 937         }
 938 
 939         @Override
 940         public String getStringData() {
 941             return null;
 942         }
 943 
 944         @Override
 945         public void setStringData(String stringData) {
 946         }
 947 
 948         @Override
 949         public String getSystemId() {
 950             return systemId;
 951         }
 952 
 953         @Override
 954         public void setSystemId(String systemId) {
 955             this.systemId = systemId;
 956         }
 957 
 958         @Override
 959         public String getPublicId() {
 960             return null;
 961         }
 962 
 963         @Override
 964         public void setPublicId(String publicId) {
 965         }
 966 
 967         @Override
 968         public String getBaseURI() {
 969             return baseUri;
 970         }
 971 
 972         @Override
 973         public void setBaseURI(String baseURI) {
 974             this.baseUri = baseURI;
 975         }
 976 
 977         @Override
 978         public String getEncoding() {
 979             return null;
 980         }
 981 
 982         @Override
 983         public void setEncoding(String encoding) {
 984         }
 985 
 986         @Override
 987         public boolean getCertifiedText() {
 988             return false;
 989         }
 990 
 991         @Override
 992         public void setCertifiedText(boolean certifiedText) {
 993         }
 994     }
 995 
 996     class XslResolver implements URIResolver {
 997 
 998         String[] hrefs;
 999         Source[] returnValues;
1000 
1001         public XslResolver(String[] href, Source[] returnValues) {
1002             this.hrefs = href;
1003             this.returnValues = returnValues;
1004         }
1005 
1006         @Override
1007         public Source resolve(String href, String base) throws TransformerException {
1008             for (int i = 0; i < hrefs.length; i++) {
1009                 if (href.endsWith(hrefs[i])) {
1010                     return returnValues[i];
1011                 }
1012             }
1013             return null;
1014         }
1015     }
1016 }