001 /**
002 * Copyright 2005-2011 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016 package org.kuali.rice.ken.xpath;
017
018 import org.apache.commons.io.IOUtils;
019 import org.apache.xerces.jaxp.JAXPConstants;
020 import org.junit.Test;
021 import org.kuali.rice.ken.test.KENTestCase;
022 import org.kuali.rice.ken.util.DocumentNamespaceContext;
023 import org.kuali.rice.ken.util.SimpleErrorHandler;
024 import org.kuali.rice.ken.util.Util;
025 import org.kuali.rice.test.BaselineTestCase.BaselineMode;
026 import org.kuali.rice.test.BaselineTestCase.Mode;
027 import org.w3c.dom.Document;
028 import org.xml.sax.InputSource;
029
030 import javax.xml.parsers.DocumentBuilder;
031 import javax.xml.parsers.DocumentBuilderFactory;
032 import javax.xml.xpath.XPath;
033 import javax.xml.xpath.XPathConstants;
034 import javax.xml.xpath.XPathFactory;
035 import java.io.IOException;
036 import java.io.InputStream;
037
038 import static org.junit.Assert.assertEquals;
039
040
041 /**
042 * Unit test that tests the affects of various document parsing (DocumentBuilderFactory)
043 * and XPath (XPath) flags, such as validation, namespace awareness, and namespace context.
044 * Lessons learned:
045 * <ul>
046 * <li>DocumentBuilder namespace awareness needs to be turned on for validation to work</li>
047 * <li>XPath absolutely requires a working NamespaceContext and qualified node names in expressions
048 * if operating against a DOM which is the result of a validating parse</li>
049 * <li>There is no apparent way to set the "default" namespace for XPath...so even when NamespaceContext
050 * is set, nodes must be qualified. The only way to obtain the "default" namespace is to explicitly
051 * qualify with an empty namespace, e.g. /:notification/:channel (which is ugly and potentially confusing)</li>
052 * <li>When deriving NamespaceContext from validated DOM, the "default" namespace must therefore be explicitly
053 * registered with a prefix (which is redundant) so the NamespaceContext lookup can succeed. The alternative
054 * is to predefine the prefix in the NamespaceContent (which can be done by using a CompositeNamespaceContenxt
055 * consisting of a ConfiguredNamespaceContext and a DocumentNamespaceContext).</li>
056 * </ul>
057 * @author Kuali Rice Team (rice.collab@kuali.org)
058 */
059 @BaselineMode(Mode.ROLLBACK_CLEAR_DB)
060 public class XPathTest extends KENTestCase {
061 private static final String TEST_XML = "sample_message_event_type.xml";
062
063 protected InputSource getTestXMLInputSource() {
064 InputStream is = XPathTest.class.getResourceAsStream(TEST_XML);
065 if (is != null) {
066 try {
067 LOG.info(IOUtils.toString(is));
068 } catch (IOException e) {
069 throw new RuntimeException(e);
070 } finally {
071 IOUtils.closeQuietly(is);
072 }
073
074 is = XPathTest.class.getResourceAsStream(TEST_XML);
075 }
076 return new InputSource(is);
077 }
078
079 protected XPath getXPath(Document doc) {
080 XPath xpath = XPathFactory.newInstance().newXPath();
081 if (doc != null) {
082 xpath.setNamespaceContext(new DocumentNamespaceContext(doc));
083 } else {
084 xpath.setNamespaceContext(Util.NOTIFICATION_NAMESPACE_CONTEXT);
085 }
086 return xpath;
087 }
088
089 protected Document getDocument(boolean namespaceAware, boolean validate) throws Exception {
090 // TODO: optimize this
091 final InputSource source = getTestXMLInputSource();
092 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
093 dbf.setValidating(validate);
094 dbf.setNamespaceAware(namespaceAware);
095 dbf.setAttribute(JAXPConstants.JAXP_SCHEMA_LANGUAGE, JAXPConstants.W3C_XML_SCHEMA);
096 DocumentBuilder db = dbf.newDocumentBuilder();
097 LOG.info("Setting entityresolver");
098 db.setEntityResolver(Util.getNotificationEntityResolver(services.getNotificationContentTypeService()));
099 db.setErrorHandler(new SimpleErrorHandler(LOG));
100 return db.parse(source);
101 }
102
103 @Test
104 public void testXPathWithPlainDOM() throws Exception {
105 Document doc = getDocument(false, false);
106 XPath xpath = getXPath(null);
107 String channelName = (String) xpath.evaluate("/notification/channel", doc.getDocumentElement(), XPathConstants.STRING);
108 assertEquals("Test Channel #1", channelName);
109 }
110 @Test
111 public void testXPathWithNamespaceAwareDOM() throws Exception {
112 Document doc = getDocument(true, false);
113 XPath xpath = getXPath(null);
114 String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
115 assertEquals("Test Channel #1", channelName);
116 }
117 @Test
118 public void testXPathWithValidatedDOMFixedNamespace() throws Exception {
119 LOG.debug("TEST");
120 Document doc = getDocument(true, true);
121 LOG.info("Default namespace: " + doc.lookupNamespaceURI(null));
122 XPath xpath = getXPath(null);
123 String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
124 assertEquals("Test Channel #1", channelName);
125 }
126 @Test
127 public void testXPathWithValidatedDOMDocNamespace() throws Exception {
128 LOG.debug("TEST");
129 Document doc = getDocument(true, true);
130 LOG.info("Default namespace: " + doc.lookupNamespaceURI(null));
131 LOG.info("default prefix: " + doc.lookupPrefix(doc.lookupNamespaceURI(null)));
132 XPath xpath = XPathFactory.newInstance().newXPath();
133 xpath.setNamespaceContext(Util.getNotificationNamespaceContext(doc));
134 String channelName = (String) xpath.evaluate("/nreq:notification/nreq:channel", doc.getDocumentElement(), XPathConstants.STRING);
135 assertEquals("Test Channel #1", channelName);
136 }
137 }