1   /*
2    * $Header$
3    * $Revision$
4    * $Date$
5    *
6    * ====================================================================
7    *
8    * Copyright 2000-2002 bob mcwhirter & James Strachan.
9    * All rights reserved.
10   *
11   * Redistribution and use in source and binary forms, with or without
12   * modification, are permitted provided that the following conditions are
13   * met:
14   * 
15   *   * Redistributions of source code must retain the above copyright
16   *     notice, this list of conditions and the following disclaimer.
17   * 
18   *   * Redistributions in binary form must reproduce the above copyright
19   *     notice, this list of conditions and the following disclaimer in the
20   *     documentation and/or other materials provided with the distribution.
21   * 
22   *   * Neither the name of the Jaxen Project nor the names of its
23   *     contributors may be used to endorse or promote products derived 
24   *     from this software without specific prior written permission.
25   * 
26   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
27   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28   * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
29   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
30   * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
31   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
32   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
35   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
36   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37   *
38   * ====================================================================
39   * This software consists of voluntary contributions made by many
40   * individuals on behalf of the Jaxen Project and was originally
41   * created by bob mcwhirter <bob@werken.com> and
42   * James Strachan <jstrachan@apache.org>.  For more information on the
43   * Jaxen Project, please see <http://www.jaxen.org/>.
44   *
45   * $Id$
46   */
47  
48  
49  package org.jaxen.test;
50  
51  import java.io.IOException;
52  
53  import javax.xml.parsers.DocumentBuilder;
54  import javax.xml.parsers.DocumentBuilderFactory;
55  import javax.xml.parsers.ParserConfigurationException;
56  
57  import junit.framework.TestCase;
58  
59  import org.jaxen.JaxenException;
60  import org.jaxen.XPath;
61  import org.jaxen.dom.DOMXPath;
62  import org.jaxen.saxpath.SAXPathException;
63  import org.jaxen.saxpath.base.XPathReader;
64  import org.jaxen.saxpath.XPathSyntaxException;
65  import org.w3c.dom.Document;
66  import org.xml.sax.SAXException;
67  
68  public class XPathReaderTest extends TestCase
69  {
70      private ConformanceXPathHandler actual;
71      private Document doc;
72  
73      private XPathReader reader;
74  
75      private String[] paths = {
76          "/foo/bar[@a='1' and @b='2']",
77          "/foo/bar[@a='1' and @b!='2']",
78          "$varname[@a='1']",
79          "//attribute::*[.!='crunchy']",
80          "'//*[contains(string(text()),\"yada yada\")]'",
81      };
82  
83      private String[][] bogusPaths = {
84          new String[]{"chyld::foo", "Expected valid axis name instead of [chyld]"},
85          new String[]{"foo/tacos()", "Expected node-type"},
86          new String[]{"foo/tacos()", "Expected node-type"},
87          new String[]{"*:foo", "Unexpected ':'"},
88          new String[]{"/foo/bar[baz", "Expected: ]"},
89          new String[]{"/cracker/cheese[(mold > 1) and (sense/taste", "Expected: )"},
90          new String[]{"//", "Location path cannot end with //"},
91          new String[]{"foo/$variable/foo", "Expected one of '.', '..', '@', '*', <QName>"}
92      };
93  
94      public XPathReaderTest( String name )
95      {
96          super( name );
97      }
98  
99      public void setUp() throws ParserConfigurationException, SAXException, IOException
100     {
101         this.reader = new XPathReader();
102 
103         this.actual = new ConformanceXPathHandler();
104 
105         this.reader.setXPathHandler( this.actual );
106         
107         DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
108         factory.setNamespaceAware(true);
109         DocumentBuilder builder = factory.newDocumentBuilder();
110         doc = builder.parse( "xml/basic.xml" );
111 
112     }
113 
114     public void tearDown()
115     {
116         this.reader = null;
117     }
118 
119     // --------------------------------------------------------------------------------
120     // --------------------------------------------------------------------------------
121 
122 
123     public void testPaths() throws SAXPathException
124     {
125 
126         for( int i = 0; i < paths.length; ++i )
127         {
128             reader.parse( paths[i] );
129         }
130     }
131 
132     public void testBogusPaths() throws SAXPathException
133     {
134 
135         for( int i = 0; i < bogusPaths.length; ++i )
136         {
137             final String[] bogusPath = bogusPaths[i];
138 
139             try
140             {
141                 reader.parse( bogusPath[0] );
142                 fail( "Should have thrown XPathSyntaxException for " + bogusPath[0]);
143             }
144             catch( XPathSyntaxException e )
145             {
146                 assertEquals( bogusPath[1], e.getMessage() );
147             }
148         }
149     }
150 
151     public void testChildrenOfNumber() throws SAXPathException
152     {
153         try
154         {
155             reader.parse( "1/child::test" );
156             fail( "Should have thrown XPathSyntaxException for 1/child::test");
157         }
158         catch( XPathSyntaxException e )
159         {
160             assertEquals( "Node-set expected", e.getMessage() );
161         }
162     }
163 
164     public void testChildIsNumber() throws SAXPathException
165     {
166         try
167         {
168             reader.parse( "jane/3" );
169             fail( "Should have thrown XPathSyntaxException for jane/3");
170         }
171         catch( XPathSyntaxException e )
172         {
173             assertEquals( "Expected one of '.', '..', '@', '*', <QName>", e.getMessage() );
174         }
175         
176     }
177 
178     public void testNumberOrNumber()
179     {
180 
181         try
182         {
183             XPath xpath = new DOMXPath( "4 | 5" );
184             xpath.selectNodes( doc );
185             fail( "Should have thrown XPathSyntaxException for 4 | 5");
186         }
187         catch( JaxenException e )
188         {
189             assertEquals( "Unions are only allowed over node-sets", e.getMessage() );
190         }
191     }
192 
193     public void testStringOrNumber()
194     {
195 
196         try
197         {
198             XPath xpath = new DOMXPath( "\"test\" | 5" );
199             xpath.selectNodes( doc );
200             fail( "Should have thrown XPathSyntaxException for \"test\" | 5");
201         }
202         catch( JaxenException e )
203         {
204             assertEquals( "Unions are only allowed over node-sets", e.getMessage() );
205         }
206     }    
207     
208     public void testStringOrString() 
209     {
210 
211         try
212         {
213             XPath xpath = new DOMXPath( "\"test\" | \"festival\"" );
214             xpath.selectNodes( doc );
215             fail( "Should have thrown XPathSyntaxException for \"test\" | 5");
216         }
217         catch( JaxenException e )
218         {
219             assertEquals( "Unions are only allowed over node-sets", e.getMessage() );
220         }
221         
222     }    
223     
224     public void testUnionofNodesAndNonNodes() 
225     {
226 
227         try
228         {
229             XPath xpath = new DOMXPath( "count(//*) | //* " );
230             xpath.selectNodes( doc );
231             fail( "Should have thrown XPathSyntaxException for \"count(//*) | //* ");
232         }
233         catch( JaxenException e )
234         {
235             assertEquals( "Unions are only allowed over node-sets", e.getMessage() );
236         }
237     }    
238     
239     public void testValidAxis() throws SAXPathException
240     {
241         reader.parse( "child::foo" );
242     }
243 
244     public void testInvalidAxis() throws SAXPathException
245     {
246 
247         try
248         {
249             reader.parse( "chyld::foo" );
250             fail( "Should have thrown XPathSyntaxException" );
251         }
252         catch( XPathSyntaxException ex )
253         {
254             assertNotNull(ex.getMessage());
255         }
256 
257     }
258 
259 /*    public void testSimpleNameStep() throws SAXPathException
260     {
261         this.text = "foo";
262         this.reader.setUpParse( this.text );
263         this.reader.step( );
264         this.expected.startNameStep( Axis.CHILD,
265                                   "",
266                                   "foo" );
267         this.expected.endNameStep();
268         assertEquals( this.expected,
269           this.actual );
270 
271     } 
272 
273     public void testNameStepWithAxisAndPrefix() throws SAXPathException
274     {
275         this.text = "parent::foo:bar";
276         this.reader.setUpParse( this.text );
277         this.reader.step( );
278         this.expected.startNameStep( Axis.PARENT,
279                                   "foo",
280                                   "bar" );
281         this.expected.endNameStep();
282         assertEquals( this.expected,
283           this.actual );
284 
285     }
286 
287     public void testNodeStepWithAxis() throws SAXPathException
288     {
289 
290         this.text = "parent::node()";
291         this.reader.setUpParse( this.text );
292         this.reader.step();
293         this.expected.startAllNodeStep( Axis.PARENT );
294         this.expected.endAllNodeStep();
295         assertEquals( this.expected,
296           this.actual );
297 
298     }
299 
300     public void testProcessingInstructionStepWithName() throws SAXPathException
301     {
302         this.text = "parent::processing-instruction('cheese')";
303         this.reader.setUpParse( this.text );
304         this.reader.step( );
305         this.expected.startProcessingInstructionNodeStep( Axis.PARENT,
306                                                            "cheese" );
307         this.expected.endProcessingInstructionNodeStep();
308         assertEquals( this.expected,
309           this.actual );
310     }
311 
312     public void testProcessingInstructionStepNoName() throws SAXPathException
313     {
314         this.text = "parent::processing-instruction()";
315         this.reader.setUpParse( this.text );
316         this.reader.step( );
317         this.expected.startProcessingInstructionNodeStep( Axis.PARENT,
318                                                        "" );
319         this.expected.endProcessingInstructionNodeStep();
320         assertEquals( this.expected,
321           this.actual );
322 
323     }
324 
325     public void testAllNodeStep() throws SAXPathException
326     {
327 
328         this.text = "parent::node()";
329         this.reader.setUpParse( this.text );
330         this.reader.step( );
331         this.expected.startAllNodeStep( Axis.PARENT );
332         this.expected.endAllNodeStep();
333         assertEquals( this.expected,
334           this.actual );
335 
336     }
337 
338     public void testTextNodeStep() throws SAXPathException
339     {
340 
341         this.text = "parent::text()";
342         this.reader.setUpParse( this.text );
343         this.reader.step( );
344         this.expected.startTextNodeStep( Axis.PARENT );
345         this.expected.endTextNodeStep();
346         assertEquals( this.expected,
347           this.actual );
348 
349     }
350 
351     public void testCommentNodeStep() throws SAXPathException
352     {
353 
354         this.text = "parent::comment()";
355         this.reader.setUpParse( this.text );
356         this.reader.step( );
357         this.expected.startCommentNodeStep( Axis.PARENT );
358         this.expected.endCommentNodeStep();
359         assertEquals( this.expected,
360           this.actual );
361 
362     }*/
363 
364     public void testLocationPathStartsWithVariable() throws SAXPathException
365     {
366 
367         reader.parse( "$variable/foo" );
368 
369     }
370 
371     public void testLocationPathStartsWithParentheses() throws SAXPathException
372     {
373 
374         reader.parse( "(//x)/foo" );
375 
376     }
377 
378     /*public void testRelativeLocationPath() throws SAXPathException
379     {
380 
381         this.text = "foo/bar/baz";
382         this.reader.setUpParse( this.text );
383         this.reader.locationPath( false );
384         this.expected.startRelativeLocationPath();
385         this.expected.startNameStep( Axis.CHILD,
386                                   "",
387                                   "foo" );
388         this.expected.endNameStep();
389         this.expected.startNameStep( Axis.CHILD,
390                                   "",
391                                   "bar" );
392         this.expected.endNameStep();
393         this.expected.startNameStep( Axis.CHILD,
394                                   "",
395                                   "baz" );
396         this.expected.endNameStep();
397         this.expected.endRelativeLocationPath();
398         assertEquals( this.expected,
399           this.actual );
400 
401     }
402 
403     public void testAbsoluteLocationPath() throws SAXPathException
404     {
405         
406         this.text = "/foo/bar/baz";
407         this.reader.setUpParse( this.text );
408         this.reader.locationPath( true );
409         this.expected.startAbsoluteLocationPath();
410         this.expected.startNameStep( Axis.CHILD,
411                                   "",
412                                   "foo" );
413         this.expected.endNameStep();
414         this.expected.startNameStep( Axis.CHILD,
415                                   "",
416                                   "bar" );
417         this.expected.endNameStep();
418         this.expected.startNameStep( Axis.CHILD,
419                                   "",
420                                   "baz" );
421         this.expected.endNameStep();
422         this.expected.endAbsoluteLocationPath();
423         assertEquals( this.expected,
424           this.actual );
425 
426     }*/
427 
428     public void testNoSpaceAfterDiv() throws JaxenException 
429     {
430         XPath xpath = new DOMXPath( "105 div10" );
431         Double result = (Double) xpath.evaluate(doc);
432         assertEquals(10.5, result.doubleValue(), 0.000001);
433     }
434 
435 
436     public void testNoSpaceAfterMod() throws JaxenException 
437     {
438         XPath xpath = new DOMXPath( "105 mod10" );
439         Double result = (Double) xpath.evaluate(doc);
440         assertEquals(5, result.intValue());
441     }
442 
443 
444     public void testNoSpaceAfterPlus() throws JaxenException 
445     {
446         XPath xpath = new DOMXPath( "105 +10" );
447         Double result = (Double) xpath.evaluate(doc);
448         assertEquals(115, result.intValue());
449     }
450     
451     public void testNoSpaceAfterAnd() throws JaxenException 
452     {
453         XPath xpath = new DOMXPath("true() andfalse()");
454         Boolean result = (Boolean) xpath.evaluate(doc);
455         assertFalse(result.booleanValue());
456     }
457     
458     public void testNoSpaceAfterOr() throws JaxenException 
459     {
460         XPath xpath = new DOMXPath("true() orfalse()");
461         Boolean result = (Boolean) xpath.evaluate(doc);
462         assertTrue(result.booleanValue());
463     }
464     
465     public void testAndImmediatelyFollowedByRelativeLocationPath() throws JaxenException  
466     {
467         XPath xpath = new DOMXPath("true() andfoo");
468         Boolean result = (Boolean) xpath.evaluate(doc);
469         assertTrue(result.booleanValue());
470     }
471     
472 }