1   /*
2    * Copyright 1999-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  package org.apache.commons.jxpath.ri.model;
17  
18  import java.util.ArrayList;
19  import java.util.Iterator;
20  import java.util.List;
21  import java.util.Locale;
22  
23  import org.apache.commons.jxpath.AbstractFactory;
24  import org.apache.commons.jxpath.ClassFunctions;
25  import org.apache.commons.jxpath.JXPathContext;
26  import org.apache.commons.jxpath.JXPathTestCase;
27  import org.apache.commons.jxpath.NestedTestBean;
28  import org.apache.commons.jxpath.Pointer;
29  import org.apache.commons.jxpath.ri.QName;
30  import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
31  import org.apache.commons.jxpath.ri.compiler.TestFunctions;
32  import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer;
33  import org.apache.commons.jxpath.ri.model.beans.PropertyPointer;
34  
35  /***
36   * Abstract superclass for Bean access with JXPath.
37   *
38   * @author Dmitri Plotnikov
39   * @version $Revision: 1.19 $ $Date: 2004/02/29 14:17:45 $
40   */
41  
42  public abstract class BeanModelTestCase extends JXPathTestCase {
43      private JXPathContext context;
44  
45      /***
46       * Construct a new instance of this test case.
47       *
48       * @param name Name of the test case
49       */
50      public BeanModelTestCase(String name) {
51          super(name);
52      }
53  
54      public void setUp() {
55  //        if (context == null) {
56              context = JXPathContext.newContext(createContextBean());
57              context.setLocale(Locale.US);
58              context.setFactory(getAbstractFactory());
59  //        }
60      }
61  
62      protected abstract Object createContextBean();
63      protected abstract AbstractFactory getAbstractFactory();
64  
65      /***
66       * Test property iterators, the core of the graph traversal engine
67       */
68      public void testIndividualIterators() {
69          testIndividual(+1, 0, true, false, 0);
70          testIndividual(-1, 0, true, false, 4);
71  
72          testIndividual(0, -1, true, true, 4);
73          testIndividual(+1, -1, true, true, 4);
74          testIndividual(-1, -1, true, true, 0);
75  
76          testIndividual(0, 1, true, false, 2);
77          testIndividual(0, 1, true, true, 1);
78  
79          testIndividual(0, 0, false, false, 4);
80          testIndividual(0, 0, false, true, 4);
81      }
82  
83      private void testIndividual(
84          int relativePropertyIndex,
85          int offset,
86          boolean useStartLocation,
87          boolean reverse,
88          int expected) 
89      {
90          PropertyOwnerPointer root =
91              (PropertyOwnerPointer) NodePointer.newNodePointer(
92                  new QName(null, "root"),
93                  createContextBean(),
94                  Locale.getDefault());
95  
96          NodeIterator it;
97  
98          PropertyPointer start = null;
99  
100         if (useStartLocation) {
101             start = root.getPropertyPointer();
102             start.setPropertyIndex(
103                 relativeProperty(start, relativePropertyIndex));
104             start.setIndex(offset);
105         }
106         it =
107             root.childIterator(
108                 new NodeNameTest(new QName(null, "integers")),
109                 reverse,
110                 start);
111 
112         int size = 0;
113         while (it.setPosition(it.getPosition() + 1)) {
114             size++;
115         }
116         assertEquals(
117             "ITERATIONS: Individual, relativePropertyIndex="
118                 + relativePropertyIndex
119                 + ", offset="
120                 + offset
121                 + ", useStartLocation="
122                 + useStartLocation
123                 + ", reverse="
124                 + reverse,
125             expected,
126             size);
127     }
128 
129     /***
130      * Test property iterators with multiple properties returned
131      */
132     public void testMultipleIterators() {
133         testMultiple(0, 0, true, false, 20);
134 
135         testMultiple(3, 0, true, false, 16);
136         testMultiple(3, -1, true, true, 8);
137         testMultiple(3, 0, true, true, 4);
138         testMultiple(0, 0, false, false, 21);
139         testMultiple(0, 0, false, true, 21);
140 
141         testMultiple(3, 1, true, false, 15);
142         testMultiple(3, 3, true, false, 13);
143     }
144 
145     private void testMultiple(
146         int propertyIndex,
147         int offset,
148         boolean useStartLocation,
149         boolean reverse,
150         int expected) 
151     {
152         PropertyOwnerPointer root =
153             (PropertyOwnerPointer) NodePointer.newNodePointer(
154                 new QName(null, "root"),
155                 createContextBean(),
156                 Locale.getDefault());
157         NodeIterator it;
158 
159         PropertyPointer start = null;
160 
161         if (useStartLocation) {
162             start = root.getPropertyPointer();
163             start.setPropertyIndex(propertyIndex);
164             start.setIndex(offset);
165         }
166         it = root.childIterator(null, reverse, start);
167 
168         int size = 0;
169         while (it.setPosition(it.getPosition() + 1)) {
170 //            System.err.println("LOC: " + it.getCurrentNodePointer());
171             size++;
172         }
173         assertEquals(
174             "ITERATIONS: Multiple, propertyIndex="
175                 + propertyIndex
176                 + ", offset="
177                 + offset
178                 + ", useStartLocation="
179                 + useStartLocation
180                 + ", reverse="
181                 + reverse,
182             expected,
183             size);
184     }
185 
186     private int relativeProperty(PropertyPointer holder, int offset) {
187         String[] names = holder.getPropertyNames();
188         for (int i = 0; i < names.length; i++) {
189             if (names[i].equals("integers")) {
190                 return i + offset;
191             }
192         }
193         return -1;
194     }
195 
196     public void testIteratePropertyArrayWithHasNext() {
197         JXPathContext context = JXPathContext.newContext(createContextBean());
198         Iterator it = context.iteratePointers("/integers");
199         List actual = new ArrayList();
200         while (it.hasNext()) {
201             actual.add(((Pointer) it.next()).asPath());
202         }
203         assertEquals(
204             "Iterating 'hasNext'/'next'<" + "/integers" + ">",
205             list(
206                 "/integers[1]",
207                 "/integers[2]",
208                 "/integers[3]",
209                 "/integers[4]"),
210             actual);
211     }
212 
213     public void testIteratePropertyArrayWithoutHasNext() {
214         JXPathContext context = JXPathContext.newContext(createContextBean());
215         Iterator it = context.iteratePointers("/integers");
216         List actual = new ArrayList();
217         for (int i = 0; i < 4; i++) {
218             actual.add(it.next().toString());
219         }
220         assertEquals(
221             "Iterating 'next'<" + "/integers" + ">",
222             list(
223                 "/integers[1]",
224                 "/integers[2]",
225                 "/integers[3]",
226                 "/integers[4]"),
227             actual);
228     }
229 
230     public void testIterateAndSet() {
231         JXPathContext context = JXPathContext.newContext(createContextBean());
232 
233         Iterator it = context.iteratePointers("beans/int");
234         int i = 5;
235         while (it.hasNext()) {
236             NodePointer pointer = (NodePointer) it.next();
237             pointer.setValue(new Integer(i++));
238         }
239 
240         it = context.iteratePointers("beans/int");
241         List actual = new ArrayList();
242         while (it.hasNext()) {
243             actual.add(((Pointer) it.next()).getValue());
244         }
245         assertEquals(
246             "Iterating <" + "beans/int" + ">",
247             list(new Integer(5), new Integer(6)),
248             actual);
249     }
250 
251     /***
252      * Test contributed by Kate Dvortsova
253      */
254     public void testIteratePointerSetValue() {
255         JXPathContext context = JXPathContext.newContext(createContextBean());
256 
257         assertXPathValue(context, "/beans[1]/name", "Name 1");
258         assertXPathValue(context, "/beans[2]/name", "Name 2");
259 
260         // Test setting via context
261         context.setValue("/beans[2]/name", "Name 2 set");
262         assertXPathValue(context, "/beans[2]/name", "Name 2 set");
263 
264         // Restore original value
265         context.setValue("/beans[2]/name", "Name 2");
266         assertXPathValue(context, "/beans[2]/name", "Name 2");
267 
268         int iterCount = 0;
269         Iterator iter = context.iteratePointers("/beans/name");
270         while (iter.hasNext()) {
271             iterCount++;
272             Pointer pointer = (Pointer) iter.next();
273             String s = (String) pointer.getValue();
274             s = s + "suffix";
275             pointer.setValue(s);
276             assertEquals("pointer.getValue", s, pointer.getValue());
277             // fails right here, the value isn't getting set in the bean.
278             assertEquals(
279                 "context.getValue",
280                 s,
281                 context.getValue(pointer.asPath()));
282         }
283         assertEquals("Iteration count", 2, iterCount);
284 
285         assertXPathValue(context, "/beans[1]/name", "Name 1suffix");
286         assertXPathValue(context, "/beans[2]/name", "Name 2suffix");
287     }
288 
289     public void testRoot() {
290         assertXPathValueAndPointer(context, "/", context.getContextBean(), "/");
291     }
292 
293     public void testAxisAncestor() {
294         // ancestor::
295         assertXPathValue(context, "int/ancestor::root = /", Boolean.TRUE);
296 
297         assertXPathValue(
298             context,
299             "count(beans/name/ancestor-or-self::node())",
300             new Double(5));
301 
302         assertXPathValue(
303             context,
304             "beans/name/ancestor-or-self::node()[3] = /",
305             Boolean.TRUE);
306     }
307 
308     public void testAxisChild() {
309         assertXPathValue(context, "boolean", Boolean.FALSE);
310 
311         assertXPathPointer(context, "boolean", "/boolean");
312 
313         assertXPathPointerIterator(context, "boolean", list("/boolean"));
314 
315         // Count elements in a child collection
316         assertXPathValue(context, "count(set)", new Double(3));
317 
318 //        assertXPathValue(context,"boolean/class/name", "java.lang.Boolean");
319 
320         // Child with namespace - should not find any
321         assertXPathValueIterator(context, "foo:boolean", list());
322 
323         // Count all children with a wildcard
324         assertXPathValue(context, "count(*)", new Double(21));
325 
326         // Same, constrained by node type = node()
327         assertXPathValue(context, "count(child::node())", new Double(21));
328     }
329 
330     public void testAxisChildNestedBean() {
331         // Nested bean
332         assertXPathValue(context, "nestedBean/name", "Name 0");
333 
334         assertXPathPointer(context, "nestedBean/name", "/nestedBean/name");
335 
336         assertXPathPointerIterator(
337             context,
338             "nestedBean/name",
339             list("/nestedBean/name"));
340     }
341 
342     public void testAxisChildNestedCollection() {
343         assertXPathValueIterator(
344             context,
345             "integers",
346             list(
347                 new Integer(1),
348                 new Integer(2),
349                 new Integer(3),
350                 new Integer(4)));
351 
352         assertXPathPointer(context, "integers", "/integers");
353 
354         assertXPathPointerIterator(
355             context,
356             "integers",
357             list(
358                 "/integers[1]",
359                 "/integers[2]",
360                 "/integers[3]",
361                 "/integers[4]"));
362     }
363 
364     public void testIndexPredicate() {
365         assertXPathValue(context, "integers[2]", new Integer(2));
366 
367         assertXPathPointer(context, "integers[2]", "/integers[2]");
368 
369         assertXPathPointerIterator(
370             context,
371             "integers[2]",
372             list("/integers[2]"));
373 
374         assertXPathValue(context, "beans[1]/name", "Name 1");
375 
376         assertXPathPointer(context, "beans[1]/name", "/beans[1]/name");
377 
378         assertXPathValueIterator(
379             context,
380             "beans[1]/strings",
381             list("String 1", "String 2", "String 3"));
382 
383         assertXPathValueIterator(
384             context,
385             "beans/strings[2]",
386             list("String 2", "String 2"));
387 
388         // Find the first match
389         assertXPathValue(context, "beans/strings[2]", "String 2");
390 
391         // Indexing in a set collected from a UnionContext
392         assertXPathValue(context, "(beans/strings[2])[1]", "String 2");
393     }
394 
395     public void testAxisDescendant() {
396         // descendant::
397         assertXPathValue(context, "count(descendant::node())", new Double(65));
398 
399         // Should not find any descendants with name root
400         assertXPathValue(context, "count(descendant::root)", new Double(0));
401 
402         assertXPathValue(context, "count(descendant::name)", new Double(7));
403     }
404 
405     public void testAxisDescendantOrSelf() {
406         // descendant-or-self::
407         assertXPathValueIterator(
408             context,
409             "descendant-or-self::name",
410             set(
411                 "Name 1",
412                 "Name 2",
413                 "Name 3",
414                 "Name 6",
415                 "Name 0",
416                 "Name 5",
417                 "Name 4"));
418 
419         // Same - abbreviated syntax
420         assertXPathValueIterator(
421             context,
422             "//name",
423             set(
424                 "Name 1",
425                 "Name 2",
426                 "Name 3",
427                 "Name 6",
428                 "Name 0",
429                 "Name 5",
430                 "Name 4"));
431 
432         // See that it actually finds self
433         assertXPathValue(
434             context,
435             "count(descendant-or-self::root)",
436             new Double(1));
437 
438         // Combine descendant-or-self:: and and self::
439         assertXPathValue(context, "count(nestedBean//.)", new Double(7));
440 
441         // Combine descendant-or-self:: and and self::name
442         assertXPathValue(context, "count(//self::beans)", new Double(2));
443 
444         // Count all nodes in the tree
445         assertXPathValue(
446             context,
447             "count(descendant-or-self::node())",
448             new Double(66));
449 
450     }
451 
452     public void testAxisFollowing() {
453         // following::
454         assertXPathValue(
455             context,
456             "count(nestedBean/strings[2]/following::node())",
457             new Double(21));
458 
459         assertXPathValue(
460             context,
461             "count(nestedBean/strings[2]/following::strings)",
462             new Double(7));
463     }
464 
465     public void testAxisFollowingSibling() {
466         // following-sibling::
467         assertXPathValue(
468             context,
469             "count(/nestedBean/following-sibling::node())",
470             new Double(8));
471 
472         assertXPathValue(
473             context,
474             "count(/nestedBean/following-sibling::object)",
475             new Double(1));
476 
477         // Combine parent:: and following-sibling::
478         assertXPathValue(
479             context,
480             "count(/nestedBean/boolean/../following-sibling::node())",
481             new Double(8));
482 
483         assertXPathValue(
484             context,
485             "count(/nestedBean/boolean/../following-sibling::object)",
486             new Double(1));
487 
488         // Combine descendant:: and following-sibling::
489         assertXPathValue(
490             context,
491             "count(/descendant::boolean/following-sibling::node())",
492             new Double(53));
493 
494         assertXPathValue(
495             context,
496             "count(/descendant::boolean/following-sibling::name)",
497             new Double(7));
498     }
499 
500     public void testAxisParent() {
501         // parent::
502         assertXPathValue(context, "count(/beans/..)", new Double(1));
503 
504         assertXPathValue(context, "count(//..)", new Double(9));
505 
506         assertXPathValue(context, "count(//../..)", new Double(2));
507 
508         assertXPathValueIterator(
509             context,
510             "//parent::beans/name",
511             list("Name 1", "Name 2"));
512     }
513 
514     public void testAxisPreceding() {
515         // preceding::
516         assertXPathValue(
517             context,
518             "count(beans[2]/int/preceding::node())",
519             new Double(8));
520 
521         assertXPathValue(
522             context,
523             "count(beans[2]/int/preceding::boolean)",
524             new Double(2));
525     }
526 
527     public void testAxisPrecedingSibling() {
528         // preceding-sibling::
529         assertXPathValue(
530             context,
531             "count(/boolean/preceding-sibling::node())",
532             new Double(2));
533 
534         assertXPathValue(
535             context,
536             "count(/nestedBean/int/../preceding-sibling::node())",
537             new Double(12));
538 
539         assertXPathValue(
540             context,
541             "count(/descendant::int/preceding-sibling::node())",
542             new Double(10));
543     }
544 
545     public void testAxisSelf() {
546         // self::
547         assertXPathValue(context, "self::node() = /", Boolean.TRUE);
548 
549         assertXPathValue(context, "self::root = /", Boolean.TRUE);
550     }
551 
552     public void testUnion() {
553         // Union - note corrected document order
554         assertXPathValueIterator(
555             context,
556             "integers | beans[1]/strings",
557             list(
558                 "String 1",
559                 "String 2",
560                 "String 3",
561                 new Integer(1),
562                 new Integer(2),
563                 new Integer(3),
564                 new Integer(4)));
565 
566         assertXPathValue(
567             context,
568             "count((integers | beans[1]/strings)[contains(., '1')])",
569             new Double(2));
570 
571         assertXPathValue(
572             context,
573             "count((integers | beans[1]/strings)[name(.) = 'strings'])",
574             new Double(3));
575 
576         // Note that the following is different from "integer[2]" -
577         // it is a filter expression
578         assertXPathValue(context, "(integers)[2]", new Integer(2));
579     }
580 
581     public void testAxisAttribute() {
582         // Attributes are just like children to beans
583         assertXPathValue(context, "count(@*)", new Double(21.0));
584 
585         // Unknown attribute
586         assertXPathValueLenient(context, "@foo", null);
587     }
588 
589     /***
590      * Testing the pseudo-attribute "name" that java beans
591      * objects appear to have.
592      */
593     public void testAttributeName() {
594         assertXPathValue(context, "nestedBean[@name = 'int']", new Integer(1));
595 
596         assertXPathPointer(
597             context,
598             "nestedBean[@name = 'int']",
599             "/nestedBean/int");
600     }
601 
602     public void testAttributeLang() {
603 
604         assertXPathValue(context, "@xml:lang", "en-US");
605 
606         assertXPathValue(context, "count(@xml:*)", new Double(1));
607 
608         assertXPathValue(context, "lang('en')", Boolean.TRUE);
609 
610         assertXPathValue(context, "lang('fr')", Boolean.FALSE);
611     }
612 
613     public void testCoreFunctions() {
614 
615         assertXPathValue(context, "boolean(boolean)", Boolean.TRUE);
616 
617         assertXPathValue(context, "boolean(boolean = false())", Boolean.TRUE);
618 
619         assertXPathValue(
620             context,
621             "boolean(integers[position() < 3])",
622             Boolean.TRUE);
623 
624         assertXPathValue(
625             context,
626             "boolean(integers[position() > 4])",
627             Boolean.FALSE);
628 
629         assertXPathValue(context, "sum(integers)", new Double(10));        
630 
631         assertXPathValueAndPointer(
632                 context,
633                 "integers[last()]",
634                 new Integer(4),
635                 "/integers[4]");
636 
637         assertXPathValueAndPointer(
638                 context,
639                 "//strings[last()]",
640                 "String 3",
641                 "/beans[1]/strings[3]");
642     }
643 
644     public void testBooleanPredicate() {
645         // use child axis
646 
647         // bean[1]/int = 1
648         // bean[2]/int = 3
649 
650         assertXPathValue(context, "beans[int > 2]/name", "Name 2");
651 
652         assertXPathValueIterator(
653             context,
654             "beans[int > 2]/name",
655             list("Name 2"));
656 
657         assertXPathValueIterator(
658             context,
659             "beans[int >= 1]/name",
660             list("Name 1", "Name 2"));
661 
662         assertXPathValueIterator(
663             context,
664             "beans[int < 2]/name",
665             list("Name 1"));
666 
667         assertXPathValueIterator(
668             context,
669             "beans[int <= 3]/name",
670             list("Name 1", "Name 2"));
671 
672         assertXPathValueIterator(
673             context,
674             "beans[1]/strings[string-length() = 8]",
675             list("String 1", "String 2", "String 3"));
676 
677         // use some fancy axis and the child axis in the predicate
678         assertXPathValueIterator(
679             context,
680             "//self::node()[name = 'Name 0']/name",
681             list("Name 0"));
682 
683         // use context-dependent function in the predicate
684         assertXPathValue(
685             context,
686             "beans/strings[name(.)='strings'][2]",
687             "String 2");
688 
689         // use context-independent function in the predicate
690         assertXPathValueIterator(
691             context,
692             "//self::node()[name(.) = concat('n', 'a', 'm', 'e')]",
693             list(
694                 "Name 1",
695                 "Name 2",
696                 "Name 3",
697                 "Name 6",
698                 "Name 0",
699                 "Name 5",
700                 "Name 4"));
701 
702         assertXPathValueIterator(
703             context,
704             "integers[position()<3]",
705             list(new Integer(1), new Integer(2)));
706             
707         context.getVariables().declareVariable(
708             "temp",
709             context.getValue("beans"));
710         
711         assertXPathValueIterator(
712             context,
713             "$temp[int < 2]/int",
714             list(new Integer(1)));
715     }
716 
717     public void testDocumentOrder() {
718         assertDocumentOrder(context, "boolean", "int", -1);
719 
720         assertDocumentOrder(context, "integers[1]", "integers[2]", -1);
721 
722         assertDocumentOrder(context, "integers[1]", "integers[1]", 0);
723 
724         assertDocumentOrder(context, "nestedBean/int", "nestedBean", 1);
725 
726         assertDocumentOrder(
727             context,
728             "nestedBean/int",
729             "nestedBean/strings",
730             -1);
731 
732         assertDocumentOrder(context, "nestedBean/int", "object/int", -1);
733     }
734 
735     public void testSetPropertyValue() {
736         // Simple property
737         assertXPathSetValue(context, "int", new Integer(2));
738 
739         // Simple property with conversion from string
740         assertXPathSetValue(context, "int", "3", new Integer(3));
741 
742         // Simple property with conversion from array
743         assertXPathSetValue(context, "int", new int[] { 4 }, new Integer(4));
744 
745         // Attribute (which is the same as a child for beans
746         assertXPathSetValue(context, "@int", new Integer(10));
747     }
748 
749     public void testSetCollectionElement() {
750         // Collection element
751         assertXPathSetValue(context, "integers[2]", new Integer(5));
752 
753         // Collection element with conversion
754         assertXPathSetValue(
755             context,
756             "integers[2]",
757             new int[] { 6 },
758             new Integer(6));
759     }
760 
761     public void testSetContextDependentNode() {
762         // Find node without using SimplePathInterpreter
763         assertXPathSetValue(
764             context,
765             "integers[position() = 1]",
766             new Integer(8));
767 
768         // Find node without using SimplePathInterpreter and set its property
769         assertXPathSetValue(
770             context,
771             "beans[name = 'Name 1']/int",
772             new Integer(9));
773 
774     }
775 
776     public void testSetNonPrimitiveValue() {
777         // First, let's see if we can set a collection element to null
778         assertXPathSetValue(context, "beans[2]", null);
779 
780         // Now, assign it a whole bean
781         context.setValue("beans[2]", new NestedTestBean("Name 9"));
782 
783         assertEquals(
784             "Modified <" + "beans[2]/name" + ">",
785             "Name 9",
786             context.getValue("beans[2]/name"));
787     }
788 
789     public void testCreatePath() {
790         context.setValue("nestedBean", null);
791 
792         // Calls factory.createObject(..., TestBean, "nestedBean")
793         assertXPathCreatePath(
794             context,
795             "/nestedBean/int",
796             new Integer(1),
797             "/nestedBean/int");
798 
799         boolean ex = false;
800         try {
801             assertXPathCreatePath(
802                 context,
803                 "/nestedBean/beans[last() + 1]",
804                 new Integer(1),
805                 "/nestedBean/beans[last() + 1]");
806         }
807         catch (Exception e) {
808             ex = true;
809         }
810         assertTrue("Exception thrown on invalid path for creation", ex);
811         
812     }
813 
814     public void testCreatePathAndSetValue() {
815         context.setValue("nestedBean", null);
816 
817         // Calls factory.createObject(..., TestBean, "nestedBean")
818         assertXPathCreatePathAndSetValue(
819             context,
820             "/nestedBean/int",
821             new Integer(2),
822             "/nestedBean/int");
823     }
824 
825     public void testCreatePathExpandNewCollection() {
826         context.setValue("beans", null);
827 
828         // Calls factory.createObject(..., testBean, "beans", 2), 
829         // then  factory.createObject(..., testBean, "beans", 2)
830         assertXPathCreatePath(
831             context,
832             "/beans[2]/int",
833             new Integer(1),
834             "/beans[2]/int");
835     }
836 
837     public void testCreatePathAndSetValueExpandNewCollection() {
838         context.setValue("beans", null);
839 
840         // Calls factory.createObject(..., testBean, "beans", 2), 
841         // then factory.createObject(..., testBean, "beans", 2)
842         assertXPathCreatePathAndSetValue(
843             context,
844             "/beans[2]/int",
845             new Integer(2),
846             "/beans[2]/int");
847     }
848 
849     public void testCreatePathExpandExistingCollection() {
850         // Calls factory.createObject(..., TestBean, "integers", 5)
851         // to expand collection
852         assertXPathCreatePathAndSetValue(
853             context,
854             "/integers[5]",
855             new Integer(3),
856             "/integers[5]");
857     }
858 
859     public void testCreatePathExpandExistingCollectionAndSetProperty() {
860         // Another, but the collection already exists
861         assertXPathCreatePath(
862             context,
863             "/beans[3]/int",
864             new Integer(1),
865             "/beans[3]/int");
866     }
867 
868     public void testCreatePathAndSetValueExpandExistingCollection() {
869         // Another, but the collection already exists
870         assertXPathCreatePathAndSetValue(
871             context,
872             "/beans[3]/int",
873             new Integer(2),
874             "/beans[3]/int");
875     }
876 
877     public void testCreatePathCreateBeanExpandCollection() {
878         context.setValue("nestedBean", null);
879 
880         // Calls factory.createObject(..., TestBean, "nestedBean")
881         // Calls factory.createObject(..., nestedBean, "strings", 2)
882         assertXPathCreatePath(
883             context,
884             "/nestedBean/strings[2]",
885             "String 2",
886             "/nestedBean/strings[2]");
887     }
888 
889     public void testCreatePathAndSetValueCreateBeanExpandCollection() {
890         context.setValue("nestedBean", null);
891 
892         // Calls factory.createObject(..., TestBean, "nestedBean")
893         // Calls factory.createObject(..., nestedBean, "strings", 2)
894         assertXPathCreatePathAndSetValue(
895             context,
896             "/nestedBean/strings[2]",
897             "Test",
898             "/nestedBean/strings[2]");
899     }
900 
901     public void testRemovePathPropertyValue() {
902         // Remove property value
903         context.removePath("nestedBean/int");
904         assertEquals(
905             "Remove property value",
906             new Integer(0),
907             context.getValue("nestedBean/int"));
908     }
909 
910     public void testRemovePathArrayElement() {
911         // Assigns a new array to the property
912         context.removePath("nestedBean/strings[1]");
913         assertEquals(
914             "Remove array element",
915             "String 2",
916             context.getValue("nestedBean/strings[1]"));
917     }
918 
919     public void testRemovePathBeanValue() {
920         context.removePath("nestedBean");
921         assertEquals(
922             "Remove collection element",
923             null,
924             context.getValue("nestedBean"));
925     }
926     
927     public void testRelativeContextRelativePath() {
928         JXPathContext relative =
929             context.getRelativeContext(context.getPointer("nestedBean"));
930         
931         assertXPathValueAndPointer(relative, 
932             "int", 
933             new Integer(1), 
934             "/nestedBean/int");
935     }
936 
937     public void testRelativeContextAbsolutePath() {
938         JXPathContext relative =
939             context.getRelativeContext(context.getPointer("nestedBean"));
940         
941         assertXPathValueAndPointer(relative, 
942             "/integers[2]", 
943             new Integer(2), 
944             "/integers[2]");
945     }
946 
947     public void testRelativeContextParent() {
948         JXPathContext relative =
949             context.getRelativeContext(context.getPointer("nestedBean"));
950         
951         assertXPathValueAndPointer(relative, 
952             "../integers[2]", 
953             new Integer(2), 
954             "/integers[2]");
955     }
956     
957     public void testRelativeContextInheritance() {
958         context.setFunctions(new ClassFunctions(TestFunctions.class, "test"));
959         JXPathContext relative =
960             context.getRelativeContext(context.getPointer("nestedBean"));
961         
962         assertXPathValue(relative, 
963             "test:countPointers(strings)", 
964             new Integer(3));
965     }
966 }