KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > commons > latka > validators > XPathValidator


1 /*
2  * Copyright 1999-2002,2004 The Apache Software Foundation.
3  *
4  * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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
17 package org.apache.commons.latka.validators;
18
19 import java.io.IOException JavaDoc;
20
21 import org.apache.commons.latka.ValidationException;
22 import org.apache.commons.latka.http.Response;
23
24 import org.jdom.Document;
25 import org.jdom.Element;
26 import org.jdom.JDOMException;
27 import org.jdom.input.SAXBuilder;
28 import org.jaxen.jdom.JDOMXPath;
29 import org.jaxen.JaxenException;
30
31 /**
32  * An XPath validator.
33  *
34  * <p>Use is of the form:</p>
35  * <p>&lt;xpath select="..." [ value="..." ] [ cond="true | false" ] /&gt;</p>
36  * <p>Where :</p>
37  * <ul>
38  * <li><code>select</code> is an XPath expression, designed to match a node in
39  * the XML body of the response.</li>
40  * <li><code>value</code> is an option value which the string value of the
41  * selected node should match.</li>
42  * <li><code>cond</code> is an optional boolean value, indicating
43  * whether the test logic is to be inverted. Defaults to
44  * <code>true</code>.</li>
45  * </ul>
46  *
47  * </p>
48  * <p>
49  * If the user has specified a {@link #setValue value}, then the XPath
50  * expression is expected to evaluate to a text or attribute node (or other
51  * textual 'leaf' nodes), in which case the selected value must match that
52  * specified.
53  * </p>
54  * <p>
55  * If no value is specified, then the XPath expression will be used to check for
56  * the <em>existence</em> of a node.
57  * </p>
58  * <p>
59  * If the XPath expression evaluates to a boolean condition (eg
60  * &lt;xpath select="foo/bar='baz'"/&gt;), then the condition will be evaluated and will
61  * result in the test passing or failing. Equivalently, an expression may be
62  * specified, and <code>value</code> used to require a specific value (eg
63  * &lt;xpath select="/foo/bar" value="baz"/&gt;).
64  * </p>
65  * <p>
66  * Finally, setting <code>cond="false"</code> negates the sense of the
67  * test, allowing one to test for the <em>nonexistence</em> or
68  * <em>inequality</em> of nodes and values.
69  * </p>
70  *
71  * @author <a HREF="mailto:jefft@apache.org">Jeff Turner</a>
72  * @author dIon Gillard
73  * @since 6 January, 2001
74  * @version $Id: XPathValidator.java 155424 2005-02-26 13:09:29Z dirkv $
75  */

76 public class XPathValidator extends BaseConditionalValidator {
77
78     // General notes:
79
// - It started out simple, honest.. =)
80
// --------------------------------------------------------------- Attributes
81

82     protected String JavaDoc _select = null;
83     protected String JavaDoc _value = null;
84     // need the last XPath result for exception generation
85
protected Object JavaDoc _lastSelected = null;
86
87     // ------------------------------------------------------------- Constructors
88

89     public XPathValidator() {
90         this(null,null,true,null);
91     }
92
93     public XPathValidator(String JavaDoc label) {
94         this(label,null,true,null);
95     }
96
97     public XPathValidator(String JavaDoc label, String JavaDoc select, boolean cond, String JavaDoc value) {
98         super(label,cond);
99         _select = select;
100         _value = value;
101     }
102
103     // ------------------------------------------------------------------ Public Methods
104

105     public void setSelect(String JavaDoc select) {
106         _select = select;
107     }
108
109     public void setValue(String JavaDoc value) {
110         _value = value;
111     }
112
113     public boolean assertTrue(Response response)
114     throws ValidationException {
115
116         JDOMXPath xpath = getJDOMXPath(_select); // compile the XPath expression
117
Document doc = getDocument(response); // retrieve the XML Document to process
118
Object JavaDoc selected = getSelectedNode(xpath, doc); // Apply the XPath to retrieve a node
119
_lastSelected = selected;
120
121         if (selected == null) {
122             return false;
123         }
124
125         // Now the fun begins, where we see if our selected node meets the criteria.
126
// There are two factors:
127
//
128
// 1) What type of object did the XPath expression return?
129
// 2) If _value is specified, ie if we're testing for _value_ or _existence_
130

131         if (selected instanceof Boolean JavaDoc) {
132             // Eg, we had an expression /foo = 'bar'
133
_log.debug("Boolean XPath expression evaluated to "+selected);
134             if (_value != null) {
135                 _log.warn("Ignoring unused value '"+_value+"'.");
136             }
137             boolean matched = ((Boolean JavaDoc)selected).booleanValue();
138
139             return matched;
140
141         } else if (selected instanceof String JavaDoc) {
142             _log.debug("XPath selected string '"+selected+"'.");
143             if (_value != null) {
144                 boolean matched = selected.equals(_value);
145
146                 return matched;
147             } else {
148                 // otherwise we only test if the node is meant to exist
149
return true;
150             }
151         } else if (selected instanceof Element) {
152             if (_log.isDebugEnabled()) {
153                 _log.debug("XPath matched element: ");
154                 _log.debug(printElement((Element)selected));
155             }
156             if (_value != null) {
157                 _log.warn("Ignoring unused value '"+_value+"'.");
158             }
159
160             // otherwise we only test if the node is meant to exist
161
return true;
162         } else {
163             // Otherwise Jaxen is returning something odd
164
if (_value != null) {
165                 // Hope that .equals() does a sensible comparison
166
boolean matched = selected.equals(_value);
167
168                 return matched;
169
170             } else {
171                 _log.warn("Selected unknown type "+selected.getClass().getName());
172                 // only test if the node (whatever it is) is meant to exist
173
return true;
174             }
175         }
176     }
177
178     public String JavaDoc generateBareExceptionMessage() {
179
180         if (_lastSelected == null) {
181             return " THAT BOOLEAN XPATH '"+_select+"' WOULD SELECT SOME NODE.";
182         }
183
184         if (_lastSelected instanceof Boolean JavaDoc) {
185             return " THAT BOOLEAN XPATH '"+_select+"' WOULD RETURN '" + getCondition() + "'.";
186         } else if (_lastSelected instanceof String JavaDoc) {
187             if (_value != null) {
188                 StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
189                 buf.append(" THAT XPATH '");
190                 buf.append(_select);
191                 buf.append("' WOULD SELECT '");
192                 buf.append(_value);
193                 buf.append("', RECEIVED '");
194                 buf.append(_lastSelected);
195                 buf.append("'.");
196                 return buf.toString();
197             } else {
198                 // otherwise we only test if the node is meant to exist
199
return " THAT XPATH '" + _select + "' WOULD SELECT SOMETHING.";
200             }
201         } else if (_lastSelected instanceof Element) {
202                 // otherwise we only test if the node is meant to exist
203
return " THAT XPATH '" + _select + "' WOULD SELECT SOMETHING.";
204         } else {
205             // Otherwise Jaxen is returning something odd
206
if (_value != null) {
207                 return " THAT XPATH EXPRESSION '"+_select+"' WOULD RETURN '" + _value +
208                        "', RETURNED UNKNOWN TYPE "+_lastSelected.getClass().getName()+ ".";
209             } else {
210                 _log.warn("Selected unknown type "+_lastSelected.getClass().getName());
211                 // only test if the node (whatever it is) is meant to exist
212
return " THAT XPATH '" + _select + "' WOULD SELECT SOMETHING.";
213             }
214         }
215     }
216
217     // ------------------------------------------------------------------ Private Methods
218

219     /**
220      * Creates a Jaxen <code>JDOMXPath</code> for a given XPath expression.
221      * @param xpathExpr The XPath expression
222      * @return A non-null Jaxen <code>JDOMXPath</code> object.
223      * @throws ValidationException if <code>xpathExpr</code> was invalid.
224      */

225     private JDOMXPath getJDOMXPath(final String JavaDoc xpathExpr)
226     throws ValidationException
227     {
228         JDOMXPath xpath = null;
229         try {
230             xpath = new JDOMXPath(xpathExpr);
231         } catch (JaxenException e) {
232             fail("Couldn't compile JDOMXPath xpathExpr "+xpathExpr+": "+e.toString());
233         }
234
235         if (xpath == null) { // this should never happen
236
fail("Null compiled XPath object");
237         }
238
239         if (_log.isDebugEnabled()) {
240             _log.debug("Using XPath expression: "+xpathExpr);
241         }
242         return xpath;
243     }
244
245     /**
246      * Creates a <code>Document</code> from the Response.
247      * @param response The (usu. HTTP) Reponse object presumably containing an XML
248      * response body.
249      * @return A non-null <code>Document</code> representing the response body.
250      * @throws ValidationException if the Response object's body did not contain
251      * well-formed XML.
252      */

253     private Document getDocument(Response response)
254     throws ValidationException
255     {
256         Document doc = null;
257         SAXBuilder builder = new SAXBuilder();
258         try {
259             doc = builder.build(response.getStream());
260         } catch (Exception JavaDoc e) {
261             if (e instanceof IOException JavaDoc || e instanceof JDOMException) {
262                 fail(e.toString());
263             } else {
264                 fail("Unknown exception caught: " + e.toString());
265             }
266         }
267         if (doc == null) { // this should never happen
268
fail("Null document");
269         }
270         if (_log.isDebugEnabled()) {
271             _log.debug("Processing doc: "+printDoc(doc));
272         }
273         return doc;
274     }
275
276     /**
277      * Apply a compiled XPath expression to an XML Document, and return the
278      * selected node.
279      * @param xpath The compiled Jaxen <code>XPath</code> object
280      * @param doc The <code>Document</code> object containing the XML.
281      * @return An object returned from Jaxen, or null if there was no match. This may be:
282      * <ul>
283      * <li>A String, if the expression selected an element with a text node child</li>
284      * <li>An <code>Element</code></li>
285      * <li>A <code>java.lang.Boolean</code>, if the XPath expression is a
286      * statement (eg /foo/bar='content')</li>
287      * <li>Anything else the Jaxen author deemed useful; ie don't assume anything</li>
288      * </ul>
289      */

290     private Object JavaDoc getSelectedNode(JDOMXPath xpath, Document doc)
291     throws ValidationException
292     {
293         Object JavaDoc selected = null;
294         try {
295             selected = xpath.selectSingleNode(doc);
296         } catch (JaxenException e) {
297             fail("XPath expression '"+_select+"' didn't match any node. "+e.toString());
298         }
299
300         return selected;
301     }
302
303     /**
304      * Utility method for returning an XML rendition of a <code>Document</code>.
305      * @param doc The Document to print
306      * @return A String of XML representing the document.
307      */

308     private String JavaDoc printDoc(final Document doc) {
309         java.io.StringWriter JavaDoc sw = new java.io.StringWriter JavaDoc();
310         try {
311             new org.jdom.output.XMLOutputter().output(doc, sw);
312         } catch (java.io.IOException JavaDoc ioe) {
313             _log.error("Could not print XML document.", ioe);
314         }
315         return sw.toString();
316     }
317
318     /**
319      * Utility method for returning an XML rendition of a <code>Element</code>.
320      * @param elem an <code>Element</code> to print.
321      * @return A String of XML representing the element.
322      */

323     private String JavaDoc printElement(final Element elem) {
324         java.io.StringWriter JavaDoc sw = new java.io.StringWriter JavaDoc();
325         Element clone = (Element)((Element)elem).clone();
326         org.jdom.output.XMLOutputter xmlOut = new org.jdom.output.XMLOutputter();
327         try {
328             xmlOut.output(new org.jdom.Document(clone), sw);
329         } catch (java.io.IOException JavaDoc ioe) {
330             _log.error("Could not print XML element.", ioe);
331         }
332         return sw.toString();
333     }
334 }
335
Popular Tags