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.axes;
17  
18  import java.util.HashMap;
19  
20  import junit.framework.TestCase;
21  
22  import org.apache.commons.jxpath.JXPathContext;
23  import org.apache.commons.jxpath.NestedTestBean;
24  import org.apache.commons.jxpath.Pointer;
25  import org.apache.commons.jxpath.TestNull;
26  import org.apache.commons.jxpath.ri.model.NodePointer;
27  import org.apache.commons.jxpath.ri.model.VariablePointer;
28  import org.apache.commons.jxpath.ri.model.beans.BeanPointer;
29  import org.apache.commons.jxpath.ri.model.beans.BeanPropertyPointer;
30  import org.apache.commons.jxpath.ri.model.beans.CollectionPointer;
31  import org.apache.commons.jxpath.ri.model.beans.NullElementPointer;
32  import org.apache.commons.jxpath.ri.model.beans.NullPointer;
33  import org.apache.commons.jxpath.ri.model.beans.NullPropertyPointer;
34  import org.apache.commons.jxpath.ri.model.beans.TestBeanFactory;
35  import org.apache.commons.jxpath.ri.model.dom.DOMNodePointer;
36  import org.apache.commons.jxpath.ri.model.dynamic.DynamicPointer;
37  import org.apache.commons.jxpath.ri.model.dynamic.DynamicPropertyPointer;
38  
39  public class SimplePathInterpreterTest extends TestCase {
40  
41      private TestBeanWithNode bean;
42      private JXPathContext context;
43  
44      /***
45       * Constructor for SimplePathInterpreterTest.
46       */
47      public SimplePathInterpreterTest(String name) {
48          super(name);
49      }
50  
51      public static void main(String[] args) {
52          junit.textui.TestRunner.run(SimplePathInterpreterTest.class);
53      }
54  
55      /***
56       * @see TestCase#setUp()
57       */
58      protected void setUp() throws Exception {
59          bean = TestBeanWithNode.createTestBeanWithDOM();
60          HashMap submap = new HashMap();
61          submap.put("key", new NestedTestBean("Name 9"));
62          submap.put("strings", bean.getNestedBean().getStrings());
63          bean.getList().add(new int[]{1, 2});
64          bean.getList().add(bean.getVendor());
65          bean.getMap().put("Key3",
66              new Object[]{
67                  new NestedTestBean("some"),
68                  new Integer(2),
69                  bean.getVendor(),
70                  submap
71              }
72          );
73          bean.getMap().put("Key4", bean.getVendor());
74          bean.getMap().put("Key5", submap);
75          bean.getMap().put("Key6", new Object[0]);
76          context = JXPathContext.newContext(null, bean);
77          context.setLenient(true);
78          context.setFactory(new TestBeanFactory());
79      }
80  
81      public void testDoStepNoPredicatesPropertyOwner() {
82          // Existing scalar property
83          assertValueAndPointer("/int",
84                  new Integer(1),
85                  "/int",
86                  "Bb",
87                  "BbB");
88  
89          // self::
90          assertValueAndPointer("/./int",
91                  new Integer(1),
92                  "/int",
93                  "Bb",
94                  "BbB");
95  
96          // Missing property
97          assertNullPointer("/foo",
98                  "/foo",
99                  "Bn");
100 
101         // existingProperty/existingScalarProperty
102         assertValueAndPointer("/nestedBean/int",
103                 new Integer(1),
104                 "/nestedBean/int",
105                 "BbBb",
106                 "BbBbB");
107 
108         // existingProperty/collectionProperty
109         assertValueAndPointer("/nestedBean/strings",
110                 bean.getNestedBean().getStrings(),
111                 "/nestedBean/strings",
112                 "BbBb",
113                 "BbBbC");
114 
115         // existingProperty/missingProperty
116         assertNullPointer("/nestedBean/foo",
117                 "/nestedBean/foo",
118                 "BbBn");
119 
120         // map/missingProperty
121         assertNullPointer("/map/foo",
122                 "/map[@name='foo']",
123                 "BbDd");
124 
125         // Existing property by search in collection
126         assertValueAndPointer("/list/int",
127                 new Integer(1),
128                 "/list[3]/int",
129                 "BbBb",
130                 "BbBbB");
131 
132         // Missing property by search in collection
133         assertNullPointer("/list/foo",
134                 "/list[1]/foo",
135                 "BbBn");
136 
137         // existingProperty/missingProperty/missingProperty
138         assertNullPointer("/nestedBean/foo/bar",
139                 "/nestedBean/foo/bar",
140                 "BbBnNn");
141 
142         // collection/existingProperty/missingProperty
143         assertNullPointer("/list/int/bar",
144                 "/list[3]/int/bar",
145                 "BbBbBn");
146 
147         // collectionProperty/missingProperty/missingProperty
148         assertNullPointer("/list/foo/bar",
149                 "/list[1]/foo/bar",
150                 "BbBnNn");
151 
152         // map/missingProperty/anotherStep
153         assertNullPointer("/map/foo/bar",
154                 "/map[@name='foo']/bar",
155                 "BbDdNn");
156 
157         // Existing dynamic property
158         assertValueAndPointer("/map/Key1",
159                 "Value 1",
160                 "/map[@name='Key1']",
161                 "BbDd",
162                 "BbDdB");
163 
164         // collectionProperty
165         assertValueAndPointer("/integers",
166                 bean.getIntegers(),
167                 "/integers",
168                 "Bb",
169                 "BbC");
170     }
171 
172     public void testDoStepNoPredicatesStandard() {
173         // Existing DOM node
174         assertValueAndPointer("/vendor/location/address/city",
175                 "Fruit Market",
176                 "/vendor/location[2]/address[1]/city[1]",
177                 "BbMMMM");
178 
179         // Missing DOM node
180         assertNullPointer("/vendor/location/address/pity",
181                 "/vendor/location[1]/address[1]/pity",
182                 "BbMMMn");
183 
184         // Missing DOM node inside a missing element
185         assertNullPointer("/vendor/location/address/itty/bitty",
186                 "/vendor/location[1]/address[1]/itty/bitty",
187                 "BbMMMnNn");
188 
189         // Missing DOM node by search for the best match
190         assertNullPointer("/vendor/location/address/city/pretty",
191                 "/vendor/location[2]/address[1]/city[1]/pretty",
192                 "BbMMMMn");
193     }
194 
195     public void testDoStepPredicatesPropertyOwner() {
196         // missingProperty[@name=foo]
197         assertNullPointer("/foo[@name='foo']",
198                 "/foo[@name='foo']",
199                 "BnNn");
200 
201         // missingProperty[index]
202         assertNullPointer("/foo[3]",
203                 "/foo[3]",
204                 "Bn");
205     }
206 
207     public void testDoStepPredicatesStandard() {
208         // Looking for an actual XML attribute called "name"
209         // nodeProperty/name[@name=value]
210         assertValueAndPointer("/vendor/contact[@name='jack']",
211                 "Jack",
212                 "/vendor/contact[2]",
213                 "BbMM");
214 
215         // Indexing in XML
216         assertValueAndPointer("/vendor/contact[2]",
217                 "Jack",
218                 "/vendor/contact[2]",
219                 "BbMM");
220 
221         // Indexing in XML, no result
222         assertNullPointer("/vendor/contact[5]",
223                 "/vendor/contact[5]",
224                 "BbMn");
225 
226         // Combination of search by name and indexing in XML
227         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
228                 "Jack Black",
229                 "/vendor/contact[4]",
230                 "BbMM");
231 
232         // Combination of search by name and indexing in XML
233         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
234                 "Jack Black",
235                 "/vendor/contact[4]",
236                 "BbMM");
237     }
238 
239     public void testDoPredicateName() {
240         // existingProperty[@name=existingProperty]
241         assertValueAndPointer("/nestedBean[@name='int']",
242                 new Integer(1),
243                 "/nestedBean/int",
244                 "BbBb",
245                 "BbBbB");
246 
247         // /self::node()[@name=existingProperty]
248         assertValueAndPointer("/.[@name='int']",
249                 new Integer(1),
250                 "/int",
251                 "Bb",
252                 "BbB");
253 
254         // dynamicProperty[@name=existingProperty]
255         assertValueAndPointer("/map[@name='Key1']",
256                 "Value 1",
257                 "/map[@name='Key1']",
258                 "BbDd",
259                 "BbDdB");
260 
261         // existingProperty[@name=collectionProperty]
262         assertValueAndPointer("/nestedBean[@name='strings']",
263                 bean.getNestedBean().getStrings(),
264                 "/nestedBean/strings",
265                 "BbBb",
266                 "BbBbC");
267 
268         // existingProperty[@name=missingProperty]
269         assertNullPointer("/nestedBean[@name='foo']",
270                 "/nestedBean[@name='foo']",
271                 "BbBn");
272 
273         // map[@name=collectionProperty]
274         assertValueAndPointer("/map[@name='Key3']",
275                 bean.getMap().get("Key3"),
276                 "/map[@name='Key3']",
277                 "BbDd",
278                 "BbDdC");
279                 
280         // map[@name=missingProperty]
281         assertNullPointer("/map[@name='foo']",
282                 "/map[@name='foo']",
283                 "BbDd");
284 
285         // collectionProperty[@name=...] (find node)
286         assertValueAndPointer("/list[@name='fruitco']",
287                 context.getValue("/vendor"),
288                 "/list[5]",
289                 "BbCM");
290 
291         // collectionProperty[@name=...] (find map entry)
292         assertValueAndPointer("/map/Key3[@name='key']/name",
293                 "Name 9",
294                 "/map[@name='Key3'][4][@name='key']/name",
295                 "BbDdCDdBb",
296                 "BbDdCDdBbB");
297 
298         // map/collectionProperty[@name...]
299         assertValueAndPointer("map/Key3[@name='fruitco']",
300                 context.getValue("/vendor"),
301                 "/map[@name='Key3'][3]",
302                 "BbDdCM");
303 
304         // Bean property -> DOM Node, name match
305         assertValueAndPointer("/vendor[@name='fruitco']",
306                 context.getValue("/vendor"),
307                 "/vendor",
308                 "BbM");
309 
310         // Bean property -> DOM Node, name mismatch
311         assertNullPointer("/vendor[@name='foo']",
312                 "/vendor[@name='foo']",
313                 "BbMn");
314 
315         assertNullPointer("/vendor[@name='foo'][3]",
316                 "/vendor[@name='foo'][3]",
317                 "BbMn");
318 
319         // existingProperty(bean)[@name=missingProperty]/anotherStep
320         assertNullPointer("/nestedBean[@name='foo']/bar",
321                 "/nestedBean[@name='foo']/bar",
322                 "BbBnNn");
323 
324         // map[@name=missingProperty]/anotherStep
325         assertNullPointer("/map[@name='foo']/bar",
326                 "/map[@name='foo']/bar",
327                 "BbDdNn");
328 
329         // existingProperty(node)[@name=missingProperty]/anotherStep
330         assertNullPointer("/vendor[@name='foo']/bar",
331                 "/vendor[@name='foo']/bar",
332                 "BbMnNn");
333 
334         // existingProperty(node)[@name=missingProperty][index]/anotherStep
335         assertNullPointer("/vendor[@name='foo'][3]/bar",
336                 "/vendor[@name='foo'][3]/bar",
337                 "BbMnNn");
338 
339         // Existing dynamic property + existing property
340         assertValueAndPointer("/map[@name='Key2'][@name='name']",
341                 "Name 6",
342                 "/map[@name='Key2']/name",
343                 "BbDdBb",
344                 "BbDdBbB");
345 
346         // Existing dynamic property + existing property + index
347         assertValueAndPointer("/map[@name='Key2'][@name='strings'][2]",
348                 "String 2",
349                 "/map[@name='Key2']/strings[2]",
350                 "BbDdBb",
351                 "BbDdBbB");
352 
353         // bean/map/map/property
354         assertValueAndPointer("map[@name='Key5'][@name='key']/name",
355                 "Name 9",
356                 "/map[@name='Key5'][@name='key']/name",
357                 "BbDdDdBb",
358                 "BbDdDdBbB");
359 
360         assertNullPointer("map[@name='Key2'][@name='foo']",
361                 "/map[@name='Key2'][@name='foo']",
362                 "BbDdBn");
363 
364         assertNullPointer("map[@name='Key2'][@name='foo'][@name='bar']",
365                 "/map[@name='Key2'][@name='foo'][@name='bar']",
366                 "BbDdBnNn");
367 
368         // bean/map/node
369         assertValueAndPointer("map[@name='Key4'][@name='fruitco']",
370                 context.getValue("/vendor"),
371                 "/map[@name='Key4']",
372                 "BbDdM");
373     }
374 
375     public void testDoPredicatesStandard() {
376         // bean/map/collection/node
377         assertValueAndPointer("map[@name='Key3'][@name='fruitco']",
378                 context.getValue("/vendor"),
379                 "/map[@name='Key3'][3]",
380                 "BbDdCM");
381 
382         // bean/map/collection/missingNode
383         assertNullPointer("map[@name='Key3'][@name='foo']",
384                 "/map[@name='Key3'][4][@name='foo']",
385                 "BbDdCDd");
386 
387         // bean/map/node
388         assertValueAndPointer("map[@name='Key4'][@name='fruitco']",
389                 context.getValue("/vendor"),
390                 "/map[@name='Key4']",
391                 "BbDdM");
392 
393         // bean/map/emptyCollection[@name=foo]
394         assertNullPointer("map[@name='Key6'][@name='fruitco']",
395                 "/map[@name='Key6'][@name='fruitco']",
396                 "BbDdCn");
397 
398         // bean/node[@name=foo][index]
399         assertValueAndPointer("/vendor/contact[@name='jack'][2]",
400                 "Jack Black",
401                 "/vendor/contact[4]",
402                 "BbMM");
403 
404         // bean/node[@name=foo][missingIndex]
405         assertNullPointer("/vendor/contact[@name='jack'][5]",
406                 "/vendor/contact[@name='jack'][5]",
407                 "BbMnNn");
408 
409         // bean/node/.[@name=foo][index]
410         assertValueAndPointer("/vendor/contact/.[@name='jack']",
411                 "Jack",
412                 "/vendor/contact[2]",
413                 "BbMM");
414     }
415 
416     public void testDoPredicateIndex() {
417         // Existing dynamic property + existing property + index
418         assertValueAndPointer("/map[@name='Key2'][@name='strings'][2]",
419                 "String 2",
420                 "/map[@name='Key2']/strings[2]",
421                 "BbDdBb",
422                 "BbDdBbB");
423 
424         // existingProperty[@name=collectionProperty][index]
425         assertValueAndPointer("/nestedBean[@name='strings'][2]",
426                 bean.getNestedBean().getStrings()[1],
427                 "/nestedBean/strings[2]",
428                 "BbBb",
429                 "BbBbB");
430 
431         // existingProperty[@name=missingProperty][index]
432         assertNullPointer("/nestedBean[@name='foo'][3]",
433                 "/nestedBean[@name='foo'][3]",
434                 "BbBn");
435 
436         // existingProperty[@name=collectionProperty][missingIndex]
437         assertNullPointer("/nestedBean[@name='strings'][5]",
438                 "/nestedBean/strings[5]",
439                 "BbBbE");
440 
441         // map[@name=collectionProperty][index]
442         assertValueAndPointer("/map[@name='Key3'][2]",
443                 new Integer(2),
444                 "/map[@name='Key3'][2]",
445                 "BbDd",
446                 "BbDdB");
447 
448         // map[@name=collectionProperty][missingIndex]
449         assertNullPointer("/map[@name='Key3'][5]",
450                 "/map[@name='Key3'][5]",
451                 "BbDdE");
452 
453         // map[@name=collectionProperty][missingIndex]/property
454         assertNullPointer("/map[@name='Key3'][5]/foo",
455                 "/map[@name='Key3'][5]/foo",
456                 "BbDdENn");
457 
458         // map[@name=map][@name=collection][index]
459         assertValueAndPointer("/map[@name='Key5'][@name='strings'][2]",
460                 "String 2",
461                 "/map[@name='Key5'][@name='strings'][2]",
462                 "BbDdDd",
463                 "BbDdDdB");
464 
465         // map[@name=map][@name=collection][missingIndex]
466         assertNullPointer("/map[@name='Key5'][@name='strings'][5]",
467                 "/map[@name='Key5'][@name='strings'][5]",
468                 "BbDdDdE");
469 
470         // Existing dynamic property + indexing
471         assertValueAndPointer("/map[@name='Key3'][2]",
472                 new Integer(2),
473                 "/map[@name='Key3'][2]",
474                 "BbDd",
475                 "BbDdB");
476 
477         // Existing dynamic property + indexing
478         assertValueAndPointer("/map[@name='Key3'][1]/name",
479                 "some",
480                 "/map[@name='Key3'][1]/name",
481                 "BbDdBb",
482                 "BbDdBbB");
483 
484         // map[@name=missingProperty][index]
485         assertNullPointer("/map[@name='foo'][3]",
486                 "/map[@name='foo'][3]",
487                 "BbDdE");
488 
489         // collectionProperty[index]
490         assertValueAndPointer("/integers[2]",
491                 new Integer(2),
492                 "/integers[2]",
493                 "Bb",
494                 "BbB");
495 
496         // existingProperty/collectionProperty[index]
497         assertValueAndPointer("/nestedBean/strings[2]",
498                 bean.getNestedBean().getStrings()[1],
499                 "/nestedBean/strings[2]",
500                 "BbBb",
501                 "BbBbB");
502 
503         // existingProperty[index]/existingProperty
504         assertValueAndPointer("/list[3]/int",
505                 new Integer(1),
506                 "/list[3]/int",
507                 "BbBb",
508                 "BbBbB");
509 
510         // existingProperty[missingIndex]
511         assertNullPointer("/list[6]",
512                 "/list[6]",
513                 "BbE");
514 
515         // existingProperty/missingProperty[index]
516         assertNullPointer("/nestedBean/foo[3]",
517                 "/nestedBean/foo[3]",
518                 "BbBn");
519 
520         // map[@name=missingProperty][index]
521         assertNullPointer("/map/foo[3]",
522                 "/map[@name='foo'][3]",
523                 "BbDdE");
524 
525         // existingProperty/collectionProperty[missingIndex]
526         assertNullPointer("/nestedBean/strings[5]",
527                 "/nestedBean/strings[5]",
528                 "BbBbE");
529 
530         // map/collectionProperty[missingIndex]/property
531         assertNullPointer("/map/Key3[5]/foo",
532                 "/map[@name='Key3'][5]/foo",
533                 "BbDdENn");
534 
535         // map[@name=map]/collection[index]
536         assertValueAndPointer("/map[@name='Key5']/strings[2]",
537                 "String 2",
538                 "/map[@name='Key5'][@name='strings'][2]",
539                 "BbDdDd",
540                 "BbDdDdB");
541 
542         // map[@name=map]/collection[missingIndex]
543         assertNullPointer("/map[@name='Key5']/strings[5]",
544                 "/map[@name='Key5'][@name='strings'][5]",
545                 "BbDdDdE");
546 
547         // scalarPropertyAsCollection[index]
548         assertValueAndPointer("/int[1]",
549                 new Integer(1),
550                 "/int",
551                 "Bb",
552                 "BbB");
553 
554         // scalarPropertyAsCollection[index]
555         assertValueAndPointer(".[1]/int",
556                 new Integer(1),
557                 "/int",
558                 "Bb",
559                 "BbB");
560     }
561 
562     public void testInterpretExpressionPath() {
563         context.getVariables().declareVariable("array", new String[]{"Value1"});
564         context.getVariables().declareVariable("testnull", new TestNull());
565 
566         assertNullPointer("$testnull/nothing[2]",
567                 "$testnull/nothing[2]",
568                 "VBbE");
569     }
570 
571     private void assertValueAndPointer(
572             String path, Object expectedValue, String expectedPath,
573             String expectedSignature)
574     {
575         assertValueAndPointer(
576             path,
577             expectedValue,
578             expectedPath,
579             expectedSignature,
580             expectedSignature);
581     }
582     
583     private void assertValueAndPointer(
584             String path, Object expectedValue, String expectedPath,
585             String expectedSignature, String expectedValueSignature)
586     {
587         Object value = context.getValue(path);
588         assertEquals("Checking value: " + path, expectedValue, value);
589 
590         Pointer pointer = context.getPointer(path);
591         assertEquals("Checking pointer: " + path,
592                 expectedPath, pointer.toString());
593 
594         assertEquals("Checking signature: " + path,
595                 expectedSignature, pointerSignature(pointer));
596         
597         Pointer vPointer = ((NodePointer) pointer).getValuePointer();
598         assertEquals("Checking value pointer signature: " + path,
599                 expectedValueSignature, pointerSignature(vPointer));
600     }
601 
602     private void assertNullPointer(String path, String expectedPath,
603             String expectedSignature)
604     {
605         Pointer pointer = context.getPointer(path);
606         assertNotNull("Null path exists: " + path,
607                     pointer);
608         assertEquals("Null path as path: " + path,
609                     expectedPath, pointer.asPath());
610         assertEquals("Checking Signature: " + path,
611                     expectedSignature, pointerSignature(pointer));
612                 
613         Pointer vPointer = ((NodePointer) pointer).getValuePointer();
614         assertTrue("Null path is null: " + path,
615                     !((NodePointer) vPointer).isActual());
616         assertEquals("Checking value pointer signature: " + path,
617                     expectedSignature + "N", pointerSignature(vPointer));
618     }
619 
620     /***
621      * Since we need to test the internal Signature of a pointer,
622      * we will get a signature which will contain a single character
623      * per pointer in the chain, representing that pointer's type.
624      */
625     private String pointerSignature(Pointer pointer) {
626         if (pointer == null) {
627             return "";
628         }
629 
630         char type = '?';
631         if (pointer instanceof NullPointer) {                 type = 'N'; }
632         else if (pointer instanceof NullPropertyPointer) {    type = 'n'; }
633         else if (pointer instanceof NullElementPointer) {     type = 'E'; }
634         else if (pointer instanceof VariablePointer) {        type = 'V'; }
635         else if (pointer instanceof CollectionPointer) {      type = 'C'; }
636         else if (pointer instanceof BeanPointer) {            type = 'B'; }
637         else if (pointer instanceof BeanPropertyPointer) {    type = 'b'; }
638         else if (pointer instanceof DynamicPointer) {         type = 'D'; }
639         else if (pointer instanceof DynamicPropertyPointer) { type = 'd'; }
640         else if (pointer instanceof DOMNodePointer) {         type = 'M'; }
641         else {
642             System.err.println("UNKNOWN TYPE: " + pointer.getClass());
643         }
644         NodePointer parent = 
645             ((NodePointer) pointer).getImmediateParentPointer();
646         return pointerSignature(parent) + type;
647     }
648 }
649