001 /** 002 * Copyright 2005-2013 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.kew.support.xstream; 017 018 import org.junit.Before; 019 import org.junit.Test; 020 import org.kuali.rice.kew.rule.xmlrouting.XPathHelper; 021 import org.kuali.rice.kew.xml.xstream.XStreamSafeEvaluator; 022 import org.w3c.dom.Document; 023 import org.w3c.dom.Node; 024 import org.w3c.dom.NodeList; 025 026 import javax.xml.parsers.DocumentBuilderFactory; 027 import javax.xml.xpath.XPath; 028 import javax.xml.xpath.XPathConstants; 029 import java.io.ByteArrayInputStream; 030 031 import static org.junit.Assert.assertEquals; 032 import static org.junit.Assert.assertNotNull; 033 034 035 public class XStreamSafeEvaluatorTest { 036 037 private static final String XML = 038 "<document>"+ 039 " <beans>"+ 040 " <testBean1>"+ 041 " <bean1Name>Bean One #1</bean1Name>"+ 042 " <bean2>" + 043 " <seeHowFarTheRabbitHoleGoes><value>20</value></seeHowFarTheRabbitHoleGoes>"+ 044 " <bean2Name>Bean Two #1</bean2Name>"+ 045 " <redPill reference=\"../../../redPill\"/>"+ 046 " <redPill reference=\"../seeHowFarTheRabbitHoleGoes\"/>"+ 047 " <redPill><value>30</value></redPill>"+ 048 " </bean2>" + 049 " </testBean1>"+ 050 " <testBean1>" + 051 " <bean1Name>Bean One #2</bean1Name>" + 052 " <bean2 reference=\"../../testBean1/bean2\"/>" + 053 " </testBean1>" + 054 " <redPill><value>10</value></redPill>" + 055 " </beans>" + 056 "</document>"; 057 058 private static final String XML2 = 059 "<document>"+ 060 " <test1 reference=\"../test2\"/>"+ 061 " <test2 reference=\"../test3\"/>"+ 062 " <test3>test3</test3>"+ 063 "</document>"; 064 065 private static final String XPATH_NO_REF = "//document/beans/testBean1/bean1Name"; 066 private static final String XPATH_THROUGH_REF = "//document/beans/testBean1/bean2/bean2Name"; 067 private static final String XPATH_RED_PILL = "//document/beans/testBean1/bean2/redPill/value"; 068 069 private static final String XPATH2_TEST1 = "/document/test1"; 070 private static final String XPATH2_TEST2 = "//test2"; 071 private static final String XPATH2_TEST3 = "//document/test3"; 072 073 private static final String XPATH_GET_FOR_RELATIVE = "/document/beans/testBean1/bean2"; 074 //private static final String XPATH_GLOBAL_VALUE = ".//value"; 075 private static final String XPATH_VALUE_20_RELATIVE = "./seeHowFarTheRabbitHoleGoes/value"; 076 private static final String XPATH_VALUE_10_20_30_RELATIVE = "./redPill/value"; 077 078 079 private Document document; 080 private XStreamSafeEvaluator eval = new XStreamSafeEvaluator(); 081 private XPath xpath; 082 083 /** 084 * Set up an XPath instance using our XPathHelper which should configure the namespace and 085 * WorkflowFunctionResolver for us. 086 */ 087 @Before 088 public void setUp() throws Exception { 089 document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(XML.getBytes())); 090 xpath = XPathHelper.newXPath(document); 091 } 092 093 @Test public void testEvaluation() throws Exception { 094 // test against straight xpath first 095 NodeList nodeList = (NodeList)xpath.evaluate(XPATH_NO_REF, document, XPathConstants.NODESET); 096 assertEquals("Should find 2 nodes.", 2, nodeList.getLength()); 097 nodeList = (NodeList)xpath.evaluate(XPATH_THROUGH_REF, document, XPathConstants.NODESET); 098 assertEquals("Should find 1 nodes.", 1, nodeList.getLength()); 099 100 // test against our evaluator, it should be able to follow our reference path 101 nodeList = eval.evaluate(XPATH_NO_REF, document); 102 assertEquals("Should find 2 nodes.", 2, nodeList.getLength()); 103 nodeList = eval.evaluate(XPATH_THROUGH_REF, document); 104 assertEquals("Should find 2 nodes.", 2, nodeList.getLength()); 105 106 // now test our evaluator exposed as an XPath function 107 nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_NO_REF), document, XPathConstants.NODESET); 108 assertEquals("Should find 2 nodes.", 2, nodeList.getLength()); 109 nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_THROUGH_REF), document, XPathConstants.NODESET); 110 assertEquals("Should find 2 nodes.", 2, nodeList.getLength()); 111 112 // now test a bit more complicated XML 113 nodeList = (NodeList)xpath.evaluate(XPATH_RED_PILL, document, XPathConstants.NODESET); 114 assertEquals("Without XStream safe evaulation, should only find 1 node.", 1, nodeList.getLength()); 115 System.out.println("\n\n\n\n"); 116 nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_RED_PILL), document, XPathConstants.NODESET); 117 assertEquals("Should have 6 nodes.", 6, nodeList.getLength()); 118 String totalValue = xpath.evaluate("sum("+wrapXStreamSafe(XPATH_RED_PILL)+")", document); 119 assertEquals("Sum should be 120.", "120", totalValue); 120 } 121 122 /** 123 * Tests the case where the last node that resulted from the evaulation of the xpath has a "reference" attribute 124 * on it. I found a bug where this last "reference" attribute was not being evaluated and resolved to the 125 * proper node. 126 * 127 * This method also does some testing of chaining of reference nodes (i.e. a node has a reference to a node 128 * which has a reference to another node). 129 */ 130 @Test public void testTerminalReferences() throws Exception { 131 document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(XML2.getBytes())); 132 xpath = XPathHelper.newXPath(document); 133 134 // test against straight xpath first 135 NodeList nodeList = (NodeList)xpath.evaluate(XPATH2_TEST1, document, XPathConstants.NODESET); 136 assertEquals("There should be one node.", 1, nodeList.getLength()); 137 assertEquals("The first node should be named 'test1'", "test1", nodeList.item(0).getNodeName()); 138 assertEquals("The node should have no children.", 0, nodeList.item(0).getChildNodes().getLength()); 139 assertNotNull("The node should have a reference attribute.", nodeList.item(0).getAttributes().getNamedItem("reference")); 140 141 String test3Value = xpath.evaluate(XPATH2_TEST3, document); 142 assertEquals("test3", test3Value); 143 144 // test using the xstreamsafe function 145 String test1Value = xpath.evaluate(wrapXStreamSafe(XPATH2_TEST1), document); 146 assertEquals("test3", test1Value); 147 148 String test2Value = xpath.evaluate(wrapXStreamSafe(XPATH2_TEST2), document); 149 assertEquals("test3", test2Value); 150 151 test3Value = xpath.evaluate(wrapXStreamSafe(XPATH2_TEST3), document); 152 assertEquals("test3", test3Value); 153 } 154 155 /** 156 * Tests the usage of XPath expressions which are evaluated relative to a particular context (i.e. starts with a "."). 157 * This tests the resolution to EN-100. 158 */ 159 @Test public void testContextRelativeExpressions() throws Exception { 160 // drill down in the document to an element which we will use as our context 161 NodeList nodeList = (NodeList)xpath.evaluate(XPATH_GET_FOR_RELATIVE, document, XPathConstants.NODESET); 162 assertEquals("There should be two nodes.", 2, nodeList.getLength()); 163 // run the same xpath through the xstreamsafe evaluator 164 nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_GET_FOR_RELATIVE), document, XPathConstants.NODESET); 165 assertEquals("There should be two nodes.", 2, nodeList.getLength()); 166 167 // now drill down using the first node in the list as the context 168 Node node = nodeList.item(0); 169 // reconstruct the XPath instance for the given root node 170 xpath = XPathHelper.newXPath(node); 171 // look for <value> elements, there should be two 172 173 // TODO we currently can't support xpath expressions that start with .// When we can, uncomment this test code. 174 // nodeList = (NodeList)xpath.evaluate(XPATH_GLOBAL_VALUE, node, XPathConstants.NODESET); 175 // assertEquals("There should be two value elements.", 2, nodeList.getLength()); 176 // // evaluate as xstreamsafe 177 // nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_GLOBAL_VALUE), node, XPathConstants.NODESET); 178 // assertEquals("There should be three value elements now.", 3, nodeList.getLength()); 179 180 // try to find the value inside of redpill section 181 String value = xpath.evaluate(XPATH_VALUE_20_RELATIVE, node); 182 assertEquals("20", value); 183 // do with xstreamsafe 184 value = xpath.evaluate(wrapXStreamSafe(XPATH_VALUE_20_RELATIVE), node); 185 assertEquals("20", value); 186 187 // test non xstreamsafe, should only be 1 node 188 nodeList = (NodeList)xpath.evaluate(XPATH_VALUE_10_20_30_RELATIVE, node, XPathConstants.NODESET); 189 assertEquals("NodeList should have 1 elements.", 1, nodeList.getLength()); 190 191 // test with xstreamsafe, should be 3 nodes 192 nodeList = (NodeList)xpath.evaluate(wrapXStreamSafe(XPATH_VALUE_10_20_30_RELATIVE), node, XPathConstants.NODESET); 193 assertEquals("NodeList should have 3 elements.", 3, nodeList.getLength()); 194 } 195 196 private String wrapXStreamSafe(String xPathExpression) { 197 return "wf:xstreamsafe('"+xPathExpression+"')"; 198 } 199 200 }