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