View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.jxpath.ri.model;
18  
19  import org.apache.commons.jxpath.AbstractFactory;
20  import org.apache.commons.jxpath.IdentityManager;
21  import org.apache.commons.jxpath.JXPathContext;
22  import org.apache.commons.jxpath.JXPathException;
23  import org.apache.commons.jxpath.JXPathTestCase;
24  import org.apache.commons.jxpath.Pointer;
25  import org.apache.commons.jxpath.Variables;
26  import org.apache.commons.jxpath.xml.DocumentContainer;
27  
28  /**
29   * Abstract superclass for pure XPath 1.0.  Subclasses
30   * apply the same XPaths to contexts using different models:
31   * DOM, JDOM etc.
32   *
33   * @author Dmitri Plotnikov
34   * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $
35   */
36  
37  public abstract class XMLModelTestCase extends JXPathTestCase {
38      protected JXPathContext context;
39  
40      public void setUp() {
41          if (context == null) {
42              DocumentContainer docCtr = createDocumentContainer();
43              context = createContext();
44              Variables vars = context.getVariables();
45              vars.declareVariable("document", docCtr.getValue());
46              vars.declareVariable("container", docCtr);
47              vars.declareVariable("element", context.getPointer("vendor/location/address/street").getNode());
48          }
49      }
50  
51      protected abstract String getModel();
52  
53      protected DocumentContainer createDocumentContainer() {
54          return new DocumentContainer(
55                  JXPathTestCase.class.getResource("Vendor.xml"),
56                  getModel());
57      }
58      
59      protected abstract AbstractFactory getAbstractFactory();
60          
61      protected JXPathContext createContext() {
62          JXPathContext context =
63              JXPathContext.newContext(createDocumentContainer());
64          context.setFactory(getAbstractFactory());
65          context.registerNamespace("product", "productNS");
66          return context;
67      }
68  
69      /**
70       * An XML signature is used to determine if we have the right result
71       * after a modification of XML by JXPath.  It is basically a piece
72       * of simplified XML.
73       */
74      protected abstract String getXMLSignature(
75          Object node,
76          boolean elements,
77          boolean attributes,
78          boolean text,
79          boolean pi);
80  
81      protected void assertXMLSignature(
82          JXPathContext context,
83          String path,
84          String signature,
85          boolean elements,
86          boolean attributes,
87          boolean text,
88          boolean pi) 
89      {
90          Object node = context.getPointer(path).getNode();
91          String sig = getXMLSignature(node, elements, attributes, text, pi);
92          assertEquals("XML Signature mismatch: ", signature, sig);
93      }
94  
95      // ------------------------------------------------ Individual Test Methods
96  
97      public void testDocumentOrder() {
98          assertDocumentOrder(
99              context,
100             "vendor/location",
101             "vendor/location/address/street",
102             -1);
103 
104         assertDocumentOrder(
105             context,
106             "vendor/location[@id = '100']",
107             "vendor/location[@id = '101']",
108             -1);
109 
110         assertDocumentOrder(
111             context,
112             "vendor//price:amount",
113             "vendor/location",
114             1);
115     }
116     
117 
118     public void testSetValue() {
119         assertXPathSetValue(
120             context,
121             "vendor/location[@id = '100']",
122             "New Text");
123 
124         assertXMLSignature(
125             context,
126             "vendor/location[@id = '100']",
127             "<E>New Text</E>",
128             false,
129             false,
130             true,
131             false);
132 
133         assertXPathSetValue(
134             context,
135             "vendor/location[@id = '101']",
136             "Replacement Text");
137 
138         assertXMLSignature(
139             context,
140             "vendor/location[@id = '101']",
141             "<E>Replacement Text</E>",
142             false,
143             false,
144             true,
145             false);
146     }
147 
148     /**
149      * Test JXPathContext.createPath() with various arguments
150      */
151     public void testCreatePath() {
152         // Create a DOM element
153         assertXPathCreatePath(
154             context,
155             "/vendor[1]/location[3]",
156             "",
157             "/vendor[1]/location[3]");
158 
159         // Create a DOM element with contents
160         assertXPathCreatePath(
161             context,
162             "/vendor[1]/location[3]/address/street",
163             "",
164             "/vendor[1]/location[3]/address[1]/street[1]");
165 
166         // Create a DOM attribute
167         assertXPathCreatePath(
168             context,
169             "/vendor[1]/location[2]/@manager",
170             "",
171             "/vendor[1]/location[2]/@manager");
172 
173         assertXPathCreatePath(
174             context,
175             "/vendor[1]/location[1]/@name",
176             "local",
177             "/vendor[1]/location[1]/@name");
178 
179          assertXPathCreatePathAndSetValue(
180             context,
181             "/vendor[1]/location[4]/@manager",
182             "",
183             "/vendor[1]/location[4]/@manager");
184          
185          context.registerNamespace("price", "priceNS");
186          
187          // Create a DOM element
188          assertXPathCreatePath(
189              context,
190              "/vendor[1]/price:foo/price:bar",
191              "",
192              "/vendor[1]/price:foo[1]/price:bar[1]");
193     }
194 
195     /**
196      * Test JXPath.createPathAndSetValue() with various arguments
197      */
198     public void testCreatePathAndSetValue() {
199         // Create a XML element
200         assertXPathCreatePathAndSetValue(
201             context,
202             "vendor/location[3]",
203             "",
204             "/vendor[1]/location[3]");
205 
206         // Create a DOM element with contents
207         assertXPathCreatePathAndSetValue(
208             context,
209             "vendor/location[3]/address/street",
210             "Lemon Circle",
211             "/vendor[1]/location[3]/address[1]/street[1]");
212 
213         // Create an attribute
214         assertXPathCreatePathAndSetValue(
215             context,
216             "vendor/location[2]/@manager",
217             "John Doe",
218             "/vendor[1]/location[2]/@manager");
219 
220         assertXPathCreatePathAndSetValue(
221             context,
222             "vendor/location[1]/@manager",
223             "John Doe",
224             "/vendor[1]/location[1]/@manager");
225         
226         assertXPathCreatePathAndSetValue(
227             context,
228             "/vendor[1]/location[4]/@manager",
229             "James Dow",
230             "/vendor[1]/location[4]/@manager");
231         
232         assertXPathCreatePathAndSetValue(
233             context,
234             "vendor/product/product:name/attribute::price:language",
235             "English",
236             "/vendor[1]/product[1]/product:name[1]/@price:language");
237         
238         context.registerNamespace("price", "priceNS");
239         
240         // Create a DOM element
241         assertXPathCreatePathAndSetValue(
242             context,
243             "/vendor[1]/price:foo/price:bar",
244             "123.20",
245             "/vendor[1]/price:foo[1]/price:bar[1]");
246     }
247 
248     /**
249      * Test JXPathContext.removePath() with various arguments
250      */
251     public void testRemovePath() {
252         // Remove XML nodes
253         context.removePath("vendor/location[@id = '101']//street/text()");
254         assertEquals(
255             "Remove DOM text",
256             "",
257             context.getValue("vendor/location[@id = '101']//street"));
258 
259         context.removePath("vendor/location[@id = '101']//street");
260         assertEquals(
261             "Remove DOM element",
262             new Double(0),
263             context.getValue("count(vendor/location[@id = '101']//street)"));
264 
265         context.removePath("vendor/location[@id = '100']/@name");
266         assertEquals(
267             "Remove DOM attribute",
268             new Double(0),
269             context.getValue("count(vendor/location[@id = '100']/@name)"));
270     }
271 
272     public void testID() {
273         context.setIdentityManager(new IdentityManager() {
274             public Pointer getPointerByID(JXPathContext context, String id) {
275                 NodePointer ptr = (NodePointer) context.getPointer("/");
276                 ptr = ptr.getValuePointer(); // Unwrap the container
277                 return ptr.getPointerByID(context, id);
278             }
279         });
280 
281         assertXPathValueAndPointer(
282             context,
283             "id(101)//street",
284             "Tangerine Drive",
285             "id('101')/address[1]/street[1]");
286 
287         assertXPathPointerLenient(
288             context,
289             "id(105)/address/street",
290             "id(105)/address/street");
291     }
292 
293     public void testAxisChild() {
294         assertXPathValue(
295             context,
296             "vendor/location/address/street",
297             "Orchard Road");
298 
299         // child:: - first child does not match, need to search
300         assertXPathValue(
301             context,
302             "vendor/location/address/city",
303             "Fruit Market");
304         
305         // local-name(qualified)
306         assertXPathValue(
307             context,
308             "local-name(vendor/product/price:amount)",
309             "amount");
310         
311         // local-name(non-qualified)
312         assertXPathValue(context, "local-name(vendor/location)", "location");
313 
314         // name (qualified)
315         assertXPathValue(
316             context,
317             "name(vendor/product/price:amount)",
318             "value:amount");
319 
320         // name (non-qualified)
321         assertXPathValue(
322             context,
323             "name(vendor/location)",
324             "location");
325 
326         // namespace-uri (qualified)
327         assertXPathValue(
328             context,
329             "namespace-uri(vendor/product/price:amount)",
330             "priceNS");
331 
332         // default namespace does not affect search
333         assertXPathValue(context, "vendor/product/prix", "934.99");
334         
335         assertXPathValue(context, "/vendor/contact[@name='jim']", "Jim");
336         
337         boolean nsv = false;
338         try {
339             context.setLenient(false);
340             context.getValue("/vendor/contact[@name='jane']");
341         }
342         catch (JXPathException ex) {
343             nsv = true;
344         }
345         assertTrue("No such value: /vendor/contact[@name='jim']", nsv);
346                 
347         nsv = false;
348         try {
349             context.setLenient(false);
350             context.getValue("/vendor/contact[@name='jane']/*");
351         }
352         catch (JXPathException ex) {
353             nsv = true;
354         }
355         assertTrue("No such value: /vendor/contact[@name='jane']/*", nsv);
356         
357         // child:: with a wildcard
358         assertXPathValue(
359             context,
360             "count(vendor/product/price:*)",
361             new Double(2));
362 
363         // child:: with the default namespace
364         assertXPathValue(context, "count(vendor/product/*)", new Double(4));
365 
366         // child:: with a qualified name
367         assertXPathValue(context, "vendor/product/price:amount", "45.95");
368         
369         // null default namespace
370         context.registerNamespace("x", "temp");
371         assertXPathValue(context, "vendor/x:pos//number", "109");
372     }
373 
374     public void testAxisChildIndexPredicate() {
375         assertXPathValue(
376             context,
377             "vendor/location[2]/address/street",
378             "Tangerine Drive");
379     }
380 
381     public void testAxisDescendant() {
382         // descendant::
383         assertXPathValue(context, "//street", "Orchard Road");
384 
385         // descendent:: with a namespace and wildcard
386         assertXPathValue(context, "count(//price:*)", new Double(2));
387 
388         assertXPathValueIterator(context, "vendor//saleEnds", list("never"));
389 
390         assertXPathValueIterator(context, "vendor//promotion", list(""));
391 
392         assertXPathValueIterator(
393             context,
394             "vendor//saleEnds[../@stores = 'all']",
395             list("never"));
396 
397         assertXPathValueIterator(
398             context,
399             "vendor//promotion[../@stores = 'all']",
400             list(""));
401     }
402     
403 //    public void testAxisDescendantDocumentOrder() {
404 //        Iterator iter = context.iteratePointers("//*");
405 //        while (iter.hasNext()) {
406 //            System.err.println(iter.next());
407 //        }
408 //    }
409 
410     public void testAxisParent() {
411         // parent::
412         assertXPathPointer(
413             context,
414             "//street/..",
415             "/vendor[1]/location[1]/address[1]");
416 
417         // parent:: (note reverse document order)
418         assertXPathPointerIterator(
419             context,
420             "//street/..",
421             list(
422                 "/vendor[1]/location[2]/address[1]",
423                 "/vendor[1]/location[1]/address[1]"));
424 
425         // parent:: with a namespace and wildcard
426         assertXPathValue(
427             context,
428             "vendor/product/price:sale/saleEnds/parent::price:*" + "/saleEnds",
429             "never");
430     }
431 
432     public void testAxisFollowingSibling() {
433         // following-sibling::
434         assertXPathValue(
435             context,
436             "vendor/location[.//employeeCount = 10]/"
437                 + "following-sibling::location//street",
438             "Tangerine Drive");
439 
440         // following-sibling:: produces the correct pointer
441         assertXPathPointer(
442             context,
443             "vendor/location[.//employeeCount = 10]/"
444                 + "following-sibling::location//street",
445             "/vendor[1]/location[2]/address[1]/street[1]");
446     }
447 
448     public void testAxisPrecedingSibling() {
449         // preceding-sibling:: produces the correct pointer
450         assertXPathPointer(
451             context,
452             "//location[2]/preceding-sibling::location//street",
453             "/vendor[1]/location[1]/address[1]/street[1]");
454     }
455 
456     public void testAxisPreceding() {
457         // preceding::
458         assertXPathPointer(
459                 context,
460                 "//location[2]/preceding-sibling::location//street",
461         "/vendor[1]/location[1]/address[1]/street[1]");
462         assertXPathPointer(context, "//location[2]/preceding::*[1]", "/vendor[1]/location[1]/employeeCount[1]");
463         assertXPathPointer(context, "//location[2]/preceding::node()[3]", "/vendor[1]/location[1]/employeeCount[1]/text()[1]");
464         assertXPathPointer(context, "//location[2]/preceding::node()[4]", "/vendor[1]/location[1]/employeeCount[1]");
465     }
466 
467     public void testAxisAttribute() {
468         // attribute::
469         assertXPathValue(context, "vendor/location/@id", "100");
470 
471         // attribute:: produces the correct pointer
472         assertXPathPointer(
473             context,
474             "vendor/location/@id",
475             "/vendor[1]/location[1]/@id");
476 
477         // iterate over attributes
478         assertXPathValueIterator(
479             context,
480             "vendor/location/@id",
481             list("100", "101"));
482 
483         // Using different prefixes for the same namespace
484         assertXPathValue(
485             context,
486             "vendor/product/price:amount/@price:discount",
487             "10%");
488         
489         // namespace uri for an attribute
490         assertXPathValue(
491             context,
492             "namespace-uri(vendor/product/price:amount/@price:discount)",
493             "priceNS");
494 
495         // local name of an attribute
496         assertXPathValue(
497             context,
498             "local-name(vendor/product/price:amount/@price:discount)",
499             "discount");
500 
501         // name for an attribute
502         assertXPathValue(
503             context,
504             "name(vendor/product/price:amount/@price:discount)",
505             "price:discount");
506 
507         // attribute:: with the default namespace
508         assertXPathValue(
509             context,
510             "vendor/product/price:amount/@discount",
511             "20%");
512 
513         // namespace uri of an attribute with the default namespace
514         assertXPathValue(
515             context,
516             "namespace-uri(vendor/product/price:amount/@discount)",
517             "");
518 
519         // local name of an attribute with the default namespace
520         assertXPathValue(
521             context,
522             "local-name(vendor/product/price:amount/@discount)",
523             "discount");
524 
525         // name of an attribute with the default namespace
526         assertXPathValue(
527             context,
528             "name(vendor/product/price:amount/@discount)",
529             "discount");
530 
531         // attribute:: with a namespace and wildcard
532         assertXPathValueIterator(
533             context,
534             "vendor/product/price:amount/@price:*",
535             list("10%"));
536 
537         // attribute:: with a wildcard
538         assertXPathValueIterator(
539             context,
540             "vendor/location[1]/@*",
541             set("100", "", "local"));
542 
543         // attribute:: with default namespace and wildcard
544         assertXPathValueIterator(
545                 context,
546                 "vendor/product/price:amount/@*",
547                 //use a set because DOM returns attrs sorted by name, JDOM by occurrence order:
548                 set("10%", "20%"));
549 
550         // attribute::node()
551         assertXPathValueIterator(
552                 context,
553                 "vendor/product/price:amount/attribute::node()",
554                 //use a set because DOM returns attrs sorted by name, JDOM by occurrence order:
555                 set("10%", "20%"));
556         
557         // attribute:: select non-ns'd attributes only
558         assertXPathValueIterator(
559             context,
560             "vendor/product/price:amount/@*[namespace-uri() = '']",
561             list("20%"));
562 
563         // Empty attribute
564         assertXPathValue(context, "vendor/location/@manager", "");
565 
566         // Missing attribute
567         assertXPathValueLenient(context, "vendor/location/@missing", null);
568 
569         // Missing attribute with namespace
570         assertXPathValueLenient(context, "vendor/location/@miss:missing", null);
571 
572         // Using attribute in a predicate
573         assertXPathValue(
574             context,
575             "vendor/location[@id='101']//street",
576             "Tangerine Drive");
577         
578         assertXPathValueIterator(
579             context,
580             "/vendor/location[1]/@*[name()!= 'manager']", list("100",
581             "local"));
582     }
583 
584     public void testAxisNamespace() {
585         // namespace::
586         assertXPathValueAndPointer(
587             context,
588             "vendor/product/prix/namespace::price",
589             "priceNS",
590             "/vendor[1]/product[1]/prix[1]/namespace::price");
591 
592         // namespace::*
593         assertXPathValue(
594             context,
595             "count(vendor/product/namespace::*)",
596             new Double(3));
597 
598         // name of namespace
599         assertXPathValue(
600             context,
601             "name(vendor/product/prix/namespace::price)",
602             "price");
603 
604         // local name of namespace
605         assertXPathValue(
606             context,
607             "local-name(vendor/product/prix/namespace::price)",
608             "price");
609     }
610 
611     public void testAxisAncestor() {
612         // ancestor::
613         assertXPathValue(
614             context,
615             "vendor/product/price:sale/saleEnds/"
616                 + "ancestor::price:sale/saleEnds",
617             "never");
618 
619         // ancestor:: with a wildcard
620         assertXPathValue(
621             context,
622             "vendor/product/price:sale/saleEnds/ancestor::price:*"
623                 + "/saleEnds",
624             "never");
625     }
626 
627     public void testAxisAncestorOrSelf() {
628         // ancestor-or-self::
629         assertXPathValue(
630             context,
631             "vendor/product/price:sale/"
632                 + "ancestor-or-self::price:sale/saleEnds",
633             "never");
634     }
635 
636     public void testAxisFollowing() {
637         assertXPathValueIterator(
638             context,
639             "vendor/contact/following::location//street",
640             list("Orchard Road", "Tangerine Drive"));
641 
642         // following:: with a namespace
643         assertXPathValue(
644             context,
645             "//location/following::price:sale/saleEnds",
646             "never");
647         assertXPathPointer(context, "//location[2]/following::node()[2]", "/vendor[1]/product[1]");
648     }
649 
650     public void testAxisSelf() {
651         // self:: with a namespace
652         assertXPathValue(
653             context,
654             "//price:sale/self::price:sale/saleEnds",
655             "never");
656 
657         // self:: with an unmatching name
658         assertXPathValueLenient(context, "//price:sale/self::x/saleEnds", null);
659     }
660 
661     public void testNodeTypeComment() {
662         // comment()
663         assertXPathValue(
664             context,
665             "//product/comment()",
666             "We are not buying this product, ever");
667     }
668 
669     public void testNodeTypeText() {
670         // text()
671         //Note that this is questionable as the XPath spec tells us "." is short for self::node() and text() is by definition _not_ a node:
672         assertXPathValue(
673             context,
674             "//product/text()[. != '']",
675             "We love this product.");
676 
677         // text() pointer
678         assertXPathPointer(
679             context,
680             "//product/text()",
681             "/vendor[1]/product[1]/text()[1]");
682 
683     }
684 
685     public void testNodeTypeProcessingInstruction() {
686         // processing-instruction() without an argument
687         assertXPathValue(
688             context,
689             "//product/processing-instruction()",
690             "do not show anybody");
691 
692         // processing-instruction() with an argument
693         assertXPathValue(
694             context,
695             "//product/processing-instruction('report')",
696             "average only");
697 
698         // processing-instruction() pointer without an argument
699         assertXPathPointer(
700             context,
701             "//product/processing-instruction('report')",
702             "/vendor[1]/product[1]/processing-instruction('report')[1]");
703 
704         // processing-instruction name
705         assertXPathValue(
706             context,
707             "name(//product/processing-instruction()[1])",
708             "security");
709     }
710 
711     public void testLang() {
712         // xml:lang built-in attribute
713         assertXPathValue(context, "//product/prix/@xml:lang", "fr");
714 
715         // lang() used the built-in xml:lang attribute
716         assertXPathValue(context, "//product/prix[lang('fr')]", "934.99");
717 
718         // Default language
719         assertXPathValue(
720             context,
721             "//product/price:sale[lang('en')]/saleEnds",
722             "never");
723     }
724 
725     public void testDocument() {
726         assertXPathValue(
727             context,
728             "$document/vendor/location[1]//street",
729             "Orchard Road");
730 
731         assertXPathPointer(
732             context,
733             "$document/vendor/location[1]//street",
734             "$document/vendor[1]/location[1]/address[1]/street[1]");
735 
736         assertXPathValue(context, "$document/vendor//street", "Orchard Road");
737     }
738 
739     public void testContainer() {
740         assertXPathValue(context, "$container/vendor//street", "Orchard Road");
741 
742         assertXPathValue(context, "$container//street", "Orchard Road");
743 
744         assertXPathPointer(
745             context,
746             "$container//street",
747             "$container/vendor[1]/location[1]/address[1]/street[1]");
748 
749         // Conversion to number
750         assertXPathValue(
751             context,
752             "number(vendor/location/employeeCount)",
753             new Double(10));
754     }
755 
756     public void testElementInVariable() {
757         assertXPathValue(context, "$element", "Orchard Road");
758     }
759 
760     public void testTypeConversions() {
761         // Implicit conversion to number
762         assertXPathValue(
763             context,
764             "vendor/location/employeeCount + 1",
765             new Double(11));
766 
767         // Implicit conversion to boolean
768         assertXPathValue(
769             context,
770             "vendor/location/employeeCount and true()",
771             Boolean.TRUE);
772     }
773 
774     public void testBooleanFunction() {
775         assertXPathValue(
776             context,
777             "boolean(vendor//saleEnds[../@stores = 'all'])",
778             Boolean.TRUE);
779 
780         assertXPathValue(
781             context,
782             "boolean(vendor//promotion[../@stores = 'all'])",
783             Boolean.TRUE);
784 
785         assertXPathValue(
786             context,
787             "boolean(vendor//promotion[../@stores = 'some'])",
788             Boolean.FALSE);
789     }
790     
791     public void testFunctionsLastAndPosition() {
792         assertXPathPointer(
793                 context,
794                 "vendor//location[last()]",
795                 "/vendor[1]/location[2]");
796     }
797 
798     public void testNamespaceMapping() {
799         context.registerNamespace("rate", "priceNS");
800         context.registerNamespace("goods", "productNS");
801 
802         assertEquals("Context node namespace resolution", 
803                 "priceNS", 
804                 context.getNamespaceURI("price"));        
805         
806         assertEquals("Registered namespace resolution", 
807                 "priceNS", 
808                 context.getNamespaceURI("rate"));
809 
810         // child:: with a namespace and wildcard
811         assertXPathValue(context, 
812                 "count(vendor/product/rate:*)", 
813                 new Double(2));
814 
815         assertXPathValue(context,
816                 "vendor[1]/product[1]/rate:amount[1]/@rate:discount", "10%");
817         assertXPathValue(context,
818                 "vendor[1]/product[1]/rate:amount[1]/@price:discount", "10%");
819         assertXPathValue(context,
820                 "vendor[1]/product[1]/price:amount[1]/@rate:discount", "10%");
821         assertXPathValue(context,
822                 "vendor[1]/product[1]/price:amount[1]/@price:discount", "10%");
823 
824         // Preference for externally registered namespace prefix
825         assertXPathValueAndPointer(context,
826                 "//product:name",
827                 "Box of oranges",
828                 "/vendor[1]/product[1]/goods:name[1]");
829         
830         // Same, but with a child context        
831         JXPathContext childCtx = 
832             JXPathContext.newContext(context, context.getContextBean());
833         assertXPathValueAndPointer(childCtx,
834                 "//product:name",
835                 "Box of oranges",
836                 "/vendor[1]/product[1]/goods:name[1]");
837         
838         // Same, but with a relative context        
839         JXPathContext relativeCtx = 
840             context.getRelativeContext(context.getPointer("/vendor"));
841         assertXPathValueAndPointer(relativeCtx,
842                 "product/product:name",
843                 "Box of oranges",
844                 "/vendor[1]/product[1]/goods:name[1]");
845     }
846 
847     public void testUnion() {
848         assertXPathValue(context, "/vendor[1]/contact[1] | /vendor[1]/contact[4]", "John");
849         assertXPathValue(context, "/vendor[1]/contact[4] | /vendor[1]/contact[1]", "John");
850     }
851 
852     public void testNodes() {
853         Pointer pointer = context.getPointer("/vendor[1]/contact[1]");
854         assertFalse(pointer.getNode().equals(pointer.getValue()));
855     }
856 }