View Javadoc

1   /**
2    * Copyright 2005-2011 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.kew.support.xstream;
17  
18  import org.junit.Before;
19  import org.junit.Test;
20  import org.kuali.rice.kew.rule.xmlrouting.XPathHelper;
21  import org.kuali.rice.kew.xml.xstream.XStreamSafeEvaluator;
22  import org.w3c.dom.Document;
23  import org.w3c.dom.Node;
24  import org.w3c.dom.NodeList;
25  
26  import javax.xml.parsers.DocumentBuilderFactory;
27  import javax.xml.xpath.XPath;
28  import javax.xml.xpath.XPathConstants;
29  import java.io.ByteArrayInputStream;
30  
31  import static org.junit.Assert.assertEquals;
32  import static org.junit.Assert.assertNotNull;
33  
34  
35  public class XStreamSafeEvaluatorTest {
36  		
37  	private static final String XML =
38  			"<document>"+
39  			"  <beans>"+
40  			"    <testBean1>"+
41  			"      <bean1Name>Bean One #1</bean1Name>"+
42  			"      <bean2>" +
43  			"        <seeHowFarTheRabbitHoleGoes><value>20</value></seeHowFarTheRabbitHoleGoes>"+
44  			"        <bean2Name>Bean Two #1</bean2Name>"+
45  			"        <redPill reference=\"../../../redPill\"/>"+
46  			"        <redPill reference=\"../seeHowFarTheRabbitHoleGoes\"/>"+
47  			"        <redPill><value>30</value></redPill>"+
48  			"      </bean2>" +
49  			"    </testBean1>"+
50  			"    <testBean1>" +
51  			"      <bean1Name>Bean One #2</bean1Name>" +
52  			"      <bean2 reference=\"../../testBean1/bean2\"/>" +
53  			"    </testBean1>" +
54  			"    <redPill><value>10</value></redPill>" +
55  			"  </beans>" +
56  			"</document>";
57  	
58  	private static final String XML2 = 
59  		"<document>"+
60  		"  <test1 reference=\"../test2\"/>"+
61  		"  <test2 reference=\"../test3\"/>"+
62  		"  <test3>test3</test3>"+
63  		"</document>";
64  
65  	private static final String XPATH_NO_REF = "//document/beans/testBean1/bean1Name";
66  	private static final String XPATH_THROUGH_REF = "//document/beans/testBean1/bean2/bean2Name"; 
67  	private static final String XPATH_RED_PILL = "//document/beans/testBean1/bean2/redPill/value";
68  	
69  	private static final String XPATH2_TEST1 = "/document/test1";
70  	private static final String XPATH2_TEST2 = "//test2";
71  	private static final String XPATH2_TEST3 = "//document/test3";
72  	
73  	private static final String XPATH_GET_FOR_RELATIVE = "/document/beans/testBean1/bean2";
74  	//private static final String XPATH_GLOBAL_VALUE = ".//value";
75  	private static final String XPATH_VALUE_20_RELATIVE = "./seeHowFarTheRabbitHoleGoes/value";
76  	private static final String XPATH_VALUE_10_20_30_RELATIVE = "./redPill/value";
77  	
78  	
79  	private Document document;
80  	private XStreamSafeEvaluator eval = new XStreamSafeEvaluator();
81  	private XPath xpath;
82  	
83  	/**
84  	 * Set up an XPath instance using our XPathHelper which should configure the namespace and
85  	 * WorkflowFunctionResolver for us.
86  	 */
87      @Before
88  	public void setUp() throws Exception {
89  		document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(XML.getBytes()));
90  		xpath = XPathHelper.newXPath(document);
91  	}
92  	
93  	@Test public void testEvaluation() throws Exception {
94  		// test against straight xpath first
95  		NodeList nodeList = (NodeList)xpath.evaluate(XPATH_NO_REF, document, XPathConstants.NODESET);
96  		assertEquals("Should find 2 nodes.", 2, nodeList.getLength());
97  		nodeList = (NodeList)xpath.evaluate(XPATH_THROUGH_REF, document, XPathConstants.NODESET);
98  		assertEquals("Should find 1 nodes.", 1, nodeList.getLength());
99  		
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 }