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