001 /**
002 * Copyright 2005-2012 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 }