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.xml;
17  
18  import static org.junit.Assert.assertEquals;
19  import static org.junit.Assert.assertNotNull;
20  import static org.junit.Assert.assertNull;
21  
22  import java.io.StringReader;
23  
24  import javax.xml.xpath.XPath;
25  import javax.xml.xpath.XPathConstants;
26  import javax.xml.xpath.XPathExpressionException;
27  import javax.xml.xpath.XPathFactory;
28  
29  import org.junit.Test;
30  import org.kuali.rice.test.BaseRiceTestCase;
31  import org.w3c.dom.Node;
32  import org.w3c.dom.NodeList;
33  import org.xml.sax.InputSource;
34  
35  
36  public class XPathTest extends BaseRiceTestCase {
37  
38  	private static final String XSTREAM_SAFE_PREFIX = "wf:xstreamsafe('";
39  	private static final String XSTREAM_SAFE_SUFFIX = "')";
40      public static final String XSTREAM_MATCH_ANYWHERE_PREFIX = "//";
41      public static final String XSTREAM_MATCH_RELATIVE_PREFIX = "./";
42  	
43      private static final String TEST_DOC = "<root name=\"root\">\n" +
44                                             "  <child name=\"child1\">\n" +
45                                             "    <child_1 name=\"child1_1\">\n" +
46                                             "      <closedSimple/>\n" +
47                                             "      <emptySimple></emptySimple>\n" +
48                                             "      <textSimple>some text 1</textSimple>\n" +
49                                             "    </child_1>\n" +
50                                             "  </child>\n" +
51                                             "  <child name=\"child2\">\n" +
52                                             "    <child_2 name=\"child2_1\">\n" +
53                                             "      <closedSimple/>\n" +
54                                             "      <emptySimple></emptySimple>\n" +
55                                             "      <textSimple>some text 2</textSimple>\n" +
56                                             "    </child_2>\n" +
57                                             "  </child>\n" +
58                                             "</root>";
59  
60      private static final String TEST_ATTRIBUTE_DOC = "<root name=\"root\">\n" +
61                                                       "  <field name=\"one\" type=\"ALL\"/>\n" +
62                                                       "  <field name=\"two\" type=\"REPORT\"/>\n" +
63                                                       "  <field name=\"three\"/>\n" +
64                                                       "</root>";
65  
66      private static final XPath XPATH = XPathFactory.newInstance().newXPath();
67  
68      private static final InputSource getTestInputSource() {
69          return new InputSource(new StringReader(TEST_DOC));
70      }
71  
72      @Test public void testAttributeAbsence() throws XPathExpressionException {
73          NodeList nodes = (NodeList) XPATH.evaluate("/root/child[not(@nonExistentAttribute)]", getTestInputSource(), XPathConstants.NODESET);
74          assertEquals(2, nodes.getLength());
75          assertEquals("child1", nodes.item(0).getAttributes().getNamedItem("name").getNodeValue());
76          assertEquals("child2", nodes.item(1).getAttributes().getNamedItem("name").getNodeValue());
77  
78          // now try with an equivalent compound expression
79          nodes = (NodeList) XPATH.evaluate("/root/*[local-name(.) = 'child' or (@nonExistentAttribute)]", getTestInputSource(), XPathConstants.NODESET);
80          assertEquals(2, nodes.getLength());
81          assertEquals("child1", nodes.item(0).getAttributes().getNamedItem("name").getNodeValue());
82          assertEquals("child2", nodes.item(1).getAttributes().getNamedItem("name").getNodeValue());
83  
84          nodes = (NodeList) XPATH.evaluate("/root/child[not(@name)]", getTestInputSource(), XPathConstants.NODE);
85          assertNull(nodes);
86  
87          // now use a more specific test document
88          nodes = (NodeList) XPATH.evaluate("/root/field[@type='ALL' or not(@type)]", new InputSource(new StringReader(TEST_ATTRIBUTE_DOC)), XPathConstants.NODESET);
89          assertEquals(2, nodes.getLength());
90          assertEquals("one", nodes.item(0).getAttributes().getNamedItem("name").getNodeValue());
91          assertEquals("three", nodes.item(1).getAttributes().getNamedItem("name").getNodeValue());
92      }
93  
94      @Test public void testSelectJustChilds() throws XPathExpressionException {
95          NodeList nodes = (NodeList) XPATH.evaluate("/root/child", getTestInputSource(), XPathConstants.NODESET);
96          assertEquals(2, nodes.getLength());
97          assertEquals("child1", nodes.item(0).getAttributes().getNamedItem("name").getNodeValue());
98          assertEquals("child2", nodes.item(1).getAttributes().getNamedItem("name").getNodeValue());
99      }
100 
101     @Test public void testSelectAbsoluteChild() throws XPathExpressionException {
102         Node node = (Node) XPATH.evaluate("/root/child", getTestInputSource(), XPathConstants.NODE);
103         assertEquals("child1", node.getAttributes().getNamedItem("name").getNodeValue());
104     }
105 
106     @Test public void testSelectAnyChild() throws XPathExpressionException {
107         Node anyNode = (Node) XPATH.evaluate("//child", getTestInputSource(), XPathConstants.NODE);
108         assertEquals("child1", anyNode.getAttributes().getNamedItem("name").getNodeValue());
109     }
110 
111     @Test public void testNonexistent()  throws XPathExpressionException {
112         final String expr = "//child/child_1/nonExistent";
113         Node nonexistent = (Node) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.NODE);
114         assertNull(nonexistent);
115         String valueOfNonexistentElement = (String) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.STRING);
116         // a non-existent element does not have a 'null' text value but a zero-length string
117         assertEquals("", valueOfNonexistentElement);
118     }
119 
120     @Test public void testClosedSimple() throws XPathExpressionException {
121         final String expr = "//child/child_1/closedSimple";
122         Node closedSimple = (Node) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.NODE);
123         assertNotNull(closedSimple);
124         assertNull(closedSimple.getFirstChild());
125         String valueOfClosedTag = (String) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.STRING);
126         // a closed element does not have a 'null' text value but a zero-length string
127         assertEquals("", valueOfClosedTag);
128     }
129 
130     @Test public void testEmptySimple() throws XPathExpressionException {
131         final String expr = "//child/child_1/emptySimple";
132         Node emptySimple = (Node) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.NODE);
133         assertNotNull(emptySimple);
134         assertNull(emptySimple.getFirstChild());
135         String valueOfEmptyTag = (String) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.STRING);
136         // a closed element does not have a 'null' text value but a zero-length string
137         assertEquals("", valueOfEmptyTag);
138     }
139 
140     @Test public void testText() throws XPathExpressionException {
141         final String expr = "//child/child_2[@name='child2_1']/textSimple";
142         final String expected = "some text 2";
143         Node textSimple = (Node) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.NODE);
144         assertNotNull(textSimple);
145         assertNotNull(textSimple.getFirstChild());
146         String valueOfTextTag = (String) XPATH.evaluate(expr, getTestInputSource(), XPathConstants.STRING);
147         // a closed element does not have a 'null' text value but a zero-length string
148         assertEquals(expected, valueOfTextTag);
149     }
150     
151 /*    @Test public void testBooleanTranslation() throws Exception {
152         String KUALI_CAMPUS_TYPE_ACTIVE_INDICATOR_XSTREAMSAFE = XSTREAM_SAFE_PREFIX + XSTREAM_MATCH_ANYWHERE_PREFIX + "campus/campusType/dataObjectMaintenanceCodeActiveIndicator" + XSTREAM_SAFE_SUFFIX;
153         String KUALI_INITIATOR_UNIVERSAL_USER_STUDENT_INDICATOR_XSTREAMSAFE = XSTREAM_SAFE_PREFIX + XSTREAM_MATCH_ANYWHERE_PREFIX + "kualiTransactionalDocumentInformation/documentInitiator/person/student" + XSTREAM_SAFE_SUFFIX;
154         DocumentContent docContent = new StandardDocumentContent(
155               "<documentContent><applicationContent><org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer>" +
156               "  <kualiTransactionalDocumentInformation>" + 
157               "  <documentInitiator>" + 
158               "     <person>" +
159               "        <student>false</student>" +
160               "        <campus class=\"org.kuali.rice.krad.bo.CampusImpl--EnhancerByCGLIB--4968dd25\">" +
161               "          <campusType>" +
162               "            <dataObjectMaintenanceCodeActiveIndicator>true</dataObjectMaintenanceCodeActiveIndicator>" +
163               "          </campusType>" + 
164               "        </campus>" + 
165               "      </person>" + 
166               "    </documentInitiator>" + 
167               "  </kualiTransactionalDocumentInformation>" + 
168               "  <document>" +
169               "    <purchaseOrderCreateDate>11-09-2007</purchaseOrderCreateDate>" +
170               "    <oldPurchaseOrderCreateDate></oldPurchaseOrderCreateDate>" +
171               "  </document>" +
172               "</org.kuali.rice.krad.workflow.KualiDocumentXmlMaterializer></applicationContent></documentContent>"
173               );
174 //        DocumentContent docContent = KualiAttributeTestUtil.getDocumentContentFromXmlFileAndPath(KualiAttributeTestUtil.PURCHASE_ORDER_DOCUMENT, KualiAttributeTestUtil.RELATIVE_PATH_IN_PROJECT_WORKFLOW, "PurchaseOrderDocument");
175         XPath xpath = XPathHelper.newXPath(docContent.getDocument());
176 
177         String valueForTrue = "Yes";
178         String valueForFalse = "No";
179 
180         // test campus active indicator field translation to 'Yes'
181         String xpathConditionStatement = KUALI_CAMPUS_TYPE_ACTIVE_INDICATOR_XSTREAMSAFE + " = 'true'";
182         String xpathExpression = constructXpathExpression(valueForTrue, valueForFalse, xpathConditionStatement);
183         String xpathResult = (String) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.STRING);
184         assertEquals("Using translated xpath expression '" + xpathExpression + "'", valueForTrue, xpathResult);
185 
186         // test user student indicator translation to 'No'
187         xpathConditionStatement = KUALI_INITIATOR_UNIVERSAL_USER_STUDENT_INDICATOR_XSTREAMSAFE + " = 'true'";
188         xpathExpression = constructXpathExpression(valueForTrue, valueForFalse, xpathConditionStatement);
189         xpathResult = (String) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.STRING);
190         assertEquals("Using translated xpath expression '" + xpathExpression + "'", valueForFalse, xpathResult);
191 
192         // test filled in date field translates to 'Yes'
193         String expression = XSTREAM_SAFE_PREFIX + XSTREAM_MATCH_ANYWHERE_PREFIX + "document/purchaseOrderCreateDate" + XSTREAM_SAFE_SUFFIX;
194         xpathConditionStatement = "boolean(" + expression + ") and not(" + expression + " = '')";
195         xpathExpression = constructXpathExpression(valueForTrue, valueForFalse, xpathConditionStatement);
196         xpathResult = (String) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.STRING);
197         assertEquals("Using translated xpath expression '" + xpathExpression + "'", valueForTrue, xpathResult);
198 
199         // test empty date field translates to 'No'
200         expression = XSTREAM_SAFE_PREFIX + XSTREAM_MATCH_ANYWHERE_PREFIX + "document/oldPurchaseOrderCreateDate" + XSTREAM_SAFE_SUFFIX;
201         xpathConditionStatement = "boolean(" + expression + ") and not(" + expression + " = '')";
202         xpathExpression = constructXpathExpression(valueForTrue, valueForFalse, xpathConditionStatement);
203         xpathResult = (String) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.STRING);
204         assertEquals("Using translated xpath expression '" + xpathExpression + "'", valueForFalse, xpathResult);
205 
206         // test non-existent date field translates to 'No'
207         expression = XSTREAM_SAFE_PREFIX + XSTREAM_MATCH_ANYWHERE_PREFIX + "document/newPurchaseOrderCreateDate" + XSTREAM_SAFE_SUFFIX;
208         xpathConditionStatement = "boolean(" + expression + ") and not(" + expression + " = '')";
209         xpathExpression = constructXpathExpression(valueForTrue, valueForFalse, xpathConditionStatement);
210         xpathResult = (String) xpath.evaluate(xpathExpression, docContent.getDocument(), XPathConstants.STRING);
211         assertEquals("Using translated xpath expression '" + xpathExpression + "'", valueForFalse, xpathResult);
212     }*/
213 
214     private String constructXpathExpression(String valueForTrue, String valueForFalse, String booleanXPathExpression) {
215         String[] xpathElementsToInsert = new String[3];
216         xpathElementsToInsert[0] = "concat( substring('" + valueForTrue + "', number(not(";
217         xpathElementsToInsert[1] = "))*string-length('" + valueForTrue + "')+1), substring('" + valueForFalse + "', number(";
218         xpathElementsToInsert[2] = ")*string-length('" + valueForFalse + "')+1))";
219 
220         StringBuffer returnableString = new StringBuffer();
221         for (int i = 0; i < xpathElementsToInsert.length; i++) {
222             String newXpathElement = xpathElementsToInsert[i];
223             returnableString.append(newXpathElement);
224 
225             /*
226              * Append the given xpath expression onto the end of the stringbuffer only in the following cases - if there is only one
227              * element in the string array - if there is more than one element in the string array and if the current element is not
228              * the last element
229              */
230             if (((i + 1) != xpathElementsToInsert.length) || (xpathElementsToInsert.length == 1)) {
231                 returnableString.append(booleanXPathExpression);
232             }
233         }
234         return returnableString.toString();
235 
236     }
237 }