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 java.util.ArrayList;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Locale;
25  import java.util.Map;
26  import java.util.Vector;
27  
28  import org.apache.commons.jxpath.JXPathContext;
29  import org.apache.commons.jxpath.JXPathTestCase;
30  import org.apache.commons.jxpath.Pointer;
31  import org.apache.commons.jxpath.TestBean;
32  import org.apache.commons.jxpath.TestMixedModelBean;
33  import org.apache.commons.jxpath.TestNull;
34  import org.apache.commons.jxpath.Variables;
35  
36  /**
37   * Tests JXPath with mixed model: beans, maps, DOM etc.
38   *
39   * @author Dmitri Plotnikov
40   * @version $Revision: 1133499 $ $Date: 2011-06-08 20:19:50 +0200 (Mi, 08 Jun 2011) $
41   */
42  public class MixedModelTest extends JXPathTestCase {
43      private JXPathContext context;
44  
45      public void setUp() {
46          TestMixedModelBean bean = new TestMixedModelBean();
47          context = JXPathContext.newContext(bean);
48          context.setFactory(new TestMixedModelFactory());
49          context.setLocale(Locale.US);
50          Variables vars = context.getVariables();
51          vars.declareVariable("string", bean.getString());
52          vars.declareVariable("bean", bean.getBean());
53          vars.declareVariable("map", bean.getMap());
54          vars.declareVariable("list", bean.getList());
55          vars.declareVariable("document", bean.getDocument());
56          vars.declareVariable("element", bean.getElement());
57          vars.declareVariable("container", bean.getContainer());
58          vars.declareVariable("testnull", new TestNull());
59  
60          int[][] matrix = new int[1][];
61          matrix[0] = new int[1];
62          matrix[0][0] = 3;
63          vars.declareVariable("matrix", matrix);
64      }
65  
66      public void testVar() {
67          context.getVariables().declareVariable("foo:bar", "baz");
68  
69          assertXPathValueAndPointer(context, 
70              "$foo:bar", 
71              "baz", 
72              "$foo:bar");
73          
74      }
75      
76      public void testVarPrimitive() {
77          assertXPathValueAndPointer(context, "$string", "string", "$string");
78      }
79  
80      public void testVarBean() {
81          assertXPathValueAndPointer(
82              context,
83              "$bean/int",
84              new Integer(1),
85              "$bean/int");
86      }
87  
88      public void testVarMap() {
89          assertXPathValueAndPointer(
90              context,
91              "$map/string",
92              "string",
93              "$map[@name='string']");
94      }
95  
96      public void testVarList() {
97          assertXPathValueAndPointer(context, "$list[1]", "string", "$list[1]");
98      }
99  
100     public void testVarDocument() {
101         assertXPathValueAndPointer(
102             context,
103             "$document/vendor/location/address/city",
104             "Fruit Market",
105             "$document/vendor[1]/location[2]/address[1]/city[1]");
106     }
107 
108     public void testVarElement() {
109         assertXPathValueAndPointer(
110             context,
111             "$element/location/address/city",
112             "Fruit Market",
113             "$element/location[2]/address[1]/city[1]");
114     }
115 
116     public void testVarContainer() {
117         assertXPathValueAndPointer(
118             context,
119             "$container/vendor/location/address/city",
120             "Fruit Market",
121             "$container/vendor[1]/location[2]/address[1]/city[1]");
122     }
123 
124     // ----------------------------------------------------------------------
125 
126     public void testBeanPrimitive() {
127         assertXPathValueAndPointer(context, "string", "string", "/string");
128     }
129 
130     public void testBeanBean() {
131         assertXPathValueAndPointer(
132             context,
133             "bean/int",
134             new Integer(1),
135             "/bean/int");
136     }
137 
138     public void testBeanMap() {
139         assertXPathValueAndPointer(
140             context,
141             "map/string",
142             "string",
143             "/map[@name='string']");
144     }
145 
146     public void testBeanList() {
147         assertXPathValueAndPointer(context, "list[1]", "string", "/list[1]");
148     }
149 
150     public void testBeanDocument() {
151         assertXPathValueAndPointer(
152             context,
153             "document/vendor/location/address/city",
154             "Fruit Market",
155             "/document/vendor[1]/location[2]/address[1]/city[1]");
156     }
157 
158     public void testBeanElement() {
159         assertXPathValueAndPointer(
160             context,
161             "element/location/address/city",
162             "Fruit Market",
163             "/element/location[2]/address[1]/city[1]");
164     }
165 
166     public void testBeanContainer() {
167         assertXPathValueAndPointer(
168             context,
169             "container/vendor/location/address/city",
170             "Fruit Market",
171             "/container/vendor[1]/location[2]/address[1]/city[1]");
172     }
173 
174     // ----------------------------------------------------------------------
175 
176     public void testMapPrimitive() {
177         assertXPathValueAndPointer(
178             context,
179             "map/string",
180             "string",
181             "/map[@name='string']");
182     }
183 
184     public void testMapBean() {
185         assertXPathValueAndPointer(
186             context,
187             "map/bean/int",
188             new Integer(1),
189             "/map[@name='bean']/int");
190     }
191 
192     public void testMapMap() {
193         assertXPathValueAndPointer(
194             context,
195             "map/map/string",
196             "string",
197             "/map[@name='map'][@name='string']");
198     }
199 
200     public void testMapList() {
201         assertXPathValueAndPointer(
202             context,
203             "map/list[1]",
204             "string",
205             "/map[@name='list'][1]");
206     }
207 
208     public void testMapDocument() {
209         assertXPathValueAndPointer(
210             context,
211             "map/document/vendor/location/address/city",
212             "Fruit Market",
213             "/map[@name='document']"
214                 + "/vendor[1]/location[2]/address[1]/city[1]");
215     }
216 
217     public void testMapElement() {
218         assertXPathValueAndPointer(
219             context,
220             "map/element/location/address/city",
221             "Fruit Market",
222             "/map[@name='element']/location[2]/address[1]/city[1]");
223     }
224 
225     public void testMapContainer() {
226         assertXPathValueAndPointer(
227             context,
228             "map/container/vendor/location/address/city",
229             "Fruit Market",
230             "/map[@name='container']"
231                 + "/vendor[1]/location[2]/address[1]/city[1]");
232     }
233 
234     // ----------------------------------------------------------------------
235 
236     public void testListPrimitive() {
237         assertXPathValueAndPointer(context, "list[1]", "string", "/list[1]");
238     }
239 
240     public void testListBean() {
241         assertXPathValueAndPointer(
242             context,
243             "list[2]/int",
244             new Integer(1),
245             "/list[2]/int");
246     }
247 
248     public void testListMap() {
249         assertXPathValueAndPointer(
250             context,
251             "list[3]/string",
252             "string",
253             "/list[3][@name='string']");
254     }
255 
256     public void testListList() {
257         /** @todo: what is this supposed to do? Should we stick to XPath,
258          *  in which case [1] is simply ignored, or Java, in which case
259          *  it is supposed to extract the first element from the list?
260          */
261 //        assertXPathValueAndPointer(context,
262 //                "list[4][1]",
263 //                "string2",
264 //                "/list[4][1]");
265 
266         assertXPathValueAndPointer(
267             context,
268             "list[4]/.[1]",
269             "string2",
270             "/list[4]/.[1]");
271     }
272 
273     public void testListDocument() {
274         assertXPathValueAndPointer(
275             context,
276             "list[5]/vendor/location/address/city",
277             "Fruit Market",
278             "/list[5]/vendor[1]/location[2]/address[1]/city[1]");
279     }
280 
281     public void testListElement() {
282         assertXPathValueAndPointer(
283             context,
284             "list[6]/location/address/city",
285             "Fruit Market",
286             "/list[6]/location[2]/address[1]/city[1]");
287     }
288 
289     public void testListContainer() {
290         assertXPathValueAndPointer(
291             context,
292             "list[7]/vendor/location/address/city",
293             "Fruit Market",
294             "/list[7]/vendor[1]/location[2]/address[1]/city[1]");
295     }
296 
297     public void testNull() {
298 
299         assertXPathPointerLenient(context, "$null", "$null");
300 
301         assertXPathPointerLenient(context, "$null[3]", "$null[3]");
302 
303         assertXPathPointerLenient(
304             context,
305             "$testnull/nothing",
306             "$testnull/nothing");
307 
308         assertXPathPointerLenient(
309             context,
310             "$testnull/nothing[2]",
311             "$testnull/nothing[2]");
312 
313         assertXPathPointerLenient(context, "beans[8]/int", "/beans[8]/int");
314 
315         assertXPathValueIterator(
316             context,
317             "$testnull/nothing[1]",
318             list(null));
319 
320         JXPathContext ctx = JXPathContext.newContext(new TestNull());
321         assertXPathValue(ctx, "nothing", null);
322 
323         assertXPathValue(ctx, "child/nothing", null);
324 
325         assertXPathValue(ctx, "array[2]", null);
326 
327         assertXPathValueLenient(ctx, "nothing/something", null);
328 
329         assertXPathValueLenient(ctx, "array[2]/something", null);
330     }
331 
332     public void testRootAsCollection() {
333         assertXPathValue(context, ".[1]/string", "string");
334     }
335 
336     public void testCreatePath() {
337         context = JXPathContext.newContext(new TestBean());
338         context.setFactory(new TestMixedModelFactory());
339 
340         TestBean bean = (TestBean) context.getContextBean();
341         bean.setMap(null);
342 
343         assertXPathCreatePath(
344             context,
345             "/map[@name='TestKey5']/nestedBean/int",
346             new Integer(1),
347             "/map[@name='TestKey5']/nestedBean/int");
348 
349         bean.setMap(null);
350         assertXPathCreatePath(
351             context,
352             "/map[@name='TestKey5']/beans[2]/int",
353             new Integer(1),
354             "/map[@name='TestKey5']/beans[2]/int");
355     }
356 
357     /**
358      * Test JXPath.iterate() with map containing an array
359      */
360     public void testIterateArray() {
361         Map map = new HashMap();
362         map.put("foo", new String[] { "a", "b", "c" });
363 
364         JXPathContext context = JXPathContext.newContext(map);
365 
366         assertXPathValueIterator(context, "foo", list("a", "b", "c"));
367     }
368 
369     public void testIteratePointersArray() {
370         Map map = new HashMap();
371         map.put("foo", new String[] { "a", "b", "c" });
372 
373         JXPathContext context = JXPathContext.newContext(map);
374 
375         Iterator it = context.iteratePointers("foo");
376         List actual = new ArrayList();
377         while (it.hasNext()) {
378             Pointer ptr = (Pointer) it.next();
379             actual.add(context.getValue(ptr.asPath()));
380         }
381         assertEquals(
382             "Iterating pointers <" + "foo" + ">",
383             list("a", "b", "c"),
384             actual);
385     }
386 
387     public void testIteratePointersArrayElementWithVariable() {
388         Map map = new HashMap();
389         map.put("foo", new String[] { "a", "b", "c" });
390 
391         JXPathContext context = JXPathContext.newContext(map);
392         context.getVariables().declareVariable("x", new Integer(2));
393         Iterator it = context.iteratePointers("foo[$x]");
394         List actual = new ArrayList();
395         while (it.hasNext()) {
396             Pointer ptr = (Pointer) it.next();
397             actual.add(context.getValue(ptr.asPath()));
398         }
399         assertEquals("Iterating pointers <" + "foo" + ">", list("b"), actual);
400     }
401 
402     public void testIterateVector() {
403         Map map = new HashMap();
404         Vector vec = new Vector();
405         vec.add(new HashMap());
406         vec.add(new HashMap());
407 
408         map.put("vec", vec);
409         JXPathContext context = JXPathContext.newContext(map);
410         assertXPathPointerIterator(
411             context,
412             "/vec",
413             list("/.[@name='vec'][1]", "/.[@name='vec'][2]"));
414     }
415 
416     public void testErrorProperty() {
417         context.getVariables().declareVariable(
418             "e",
419             new ExceptionPropertyTestBean());
420 
421         boolean ex = false;
422         try {
423             assertXPathValue(context, "$e/errorString", null);
424         }
425         catch (Throwable t) {
426             ex = true;
427         }
428         assertTrue("Legitimate exception accessing property", ex);
429 
430         assertXPathPointer(context, "$e/errorString", "$e/errorString");
431 
432         assertXPathPointerLenient(
433             context,
434             "$e/errorStringArray[1]",
435             "$e/errorStringArray[1]");
436 
437         assertXPathPointerIterator(
438             context,
439             "$e/errorString",
440             list("$e/errorString"));
441 
442         assertXPathPointerIterator(
443             context,
444             "$e//error",
445             Collections.EMPTY_LIST);
446     }
447 
448     public void testMatrix() {
449         assertXPathValueAndPointer(
450             context,
451             "$matrix[1]/.[1]",
452             new Integer(3),
453             "$matrix[1]/.[1]");
454 
455         context.setValue("$matrix[1]/.[1]", new Integer(2));
456 
457         assertXPathValueAndPointer(
458             context,
459             "matrix[1]/.[1]",
460             new Integer(3),
461             "/matrix[1]/.[1]");
462 
463         context.setValue("matrix[1]/.[1]", "2");
464 
465         assertXPathValue(context, "matrix[1]/.[1]", new Integer(2));
466 
467         context.getVariables().declareVariable(
468             "wholebean",
469             context.getContextBean());
470 
471         assertXPathValueAndPointer(
472             context,
473             "$wholebean/matrix[1]/.[1]",
474             new Integer(2),
475             "$wholebean/matrix[1]/.[1]");
476 
477         boolean ex = false;
478         try {
479             context.setValue("$wholebean/matrix[1]/.[2]", "4");
480         }
481         catch (Exception e) {
482             ex = true;
483         }
484         assertTrue("Exception setting value of non-existent element", ex);
485 
486         ex = false;
487         try {
488             context.setValue("$wholebean/matrix[2]/.[1]", "4");
489         }
490         catch (Exception e) {
491             ex = true;
492         }
493         assertTrue("Exception setting value of non-existent element", ex);
494     }
495 
496     public void testCreatePathAndSetValueWithMatrix() {
497 
498         context.setValue("matrix", null);
499 
500         // Calls factory.createObject(..., TestMixedModelBean, "matrix")
501         // Calls factory.createObject(..., nestedBean, "strings", 2)
502         assertXPathCreatePathAndSetValue(
503             context,
504             "/matrix[1]/.[1]",
505             new Integer(4),
506             "/matrix[1]/.[1]");
507     }
508     
509     /**
510      * Scott Heaberlin's test - collection of collections
511      */
512     public void testCollectionPointer() {
513         List list = new ArrayList();
514         Map map = new HashMap();
515         map.put("KeyOne", "SomeStringOne");
516         map.put("KeyTwo", "SomeStringTwo");
517         
518         Map map2 = new HashMap();
519         map2.put("KeyA", "StringA");
520         map2.put("KeyB", "StringB");
521         
522         map.put("KeyThree", map2);
523         list.add(map);
524         
525         List list2 = new ArrayList();
526         list2.add("foo");
527         list2.add(map);
528         list2.add(map);
529         list.add(list2);
530         
531         context = JXPathContext.newContext(list);
532         
533         assertEquals("SomeStringOne", context.getValue(".[1]/KeyOne"));
534         assertEquals("StringA", context.getValue(".[1]/KeyThree/KeyA"));
535         assertEquals(new Integer(3), context.getValue("size(.[1]/KeyThree)"));
536         assertEquals(new Double(6.0), context.getValue("count(.[1]/KeyThree/*)"));
537         assertEquals(new Double(3.0), context.getValue("count(.[1]/KeyThree/KeyA)"));
538     }
539 }