1   /*
2    * Copyright 2002,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.jexl;
17  
18  import java.io.StringReader;
19  import java.math.BigDecimal;
20  import java.math.BigInteger;
21  import java.util.ArrayList;
22  import java.util.BitSet;
23  import java.util.Calendar;
24  import java.util.Collections;
25  import java.util.GregorianCalendar;
26  import java.util.HashMap;
27  import java.util.HashSet;
28  import java.util.LinkedList;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Set;
32  
33  import junit.framework.Test;
34  import junit.framework.TestCase;
35  import junit.framework.TestSuite;
36  
37  import org.apache.commons.jexl.parser.ParseException;
38  import org.apache.commons.jexl.parser.Parser;
39  import org.apache.commons.jexl.resolver.FlatResolver;
40  
41  /***
42   *  Simple testcases
43   *
44   *  @since 1.0
45   *  @author <a href="mailto:geirm@apache.org">Geir Magnusson Jr.</a>
46   *  @version $Id: JexlTest.java 391888 2006-04-06 03:34:59Z dion $
47   */
48  public class JexlTest extends TestCase
49  {
50      protected static final String METHOD_STRING = "Method string";
51      protected static final String GET_METHOD_STRING = "GetMethod string";
52  
53      protected static final String[] GET_METHOD_ARRAY =
54          new String[] { "One", "Two", "Three" };
55  
56      protected static final String[][] GET_METHOD_ARRAY2 =
57          new String[][] { {"One", "Two", "Three"},{"Four", "Five", "Six"} };
58  
59      public static Test suite()
60      {
61          return new TestSuite(JexlTest.class);
62      }
63  
64      public JexlTest(String testName)
65      {
66          super(testName);
67      }
68  
69      /***
70        *  test a simple property expression
71        */
72      public void testProperty()
73           throws Exception
74      {
75          /*
76           *  tests a simple property expression
77           */
78  
79          Expression e = ExpressionFactory.createExpression("foo.bar");
80          JexlContext jc = JexlHelper.createContext();
81  
82          jc.getVars().put("foo", new Foo() );
83          Object o = e.evaluate(jc);
84  
85          assertTrue("o not instanceof String", o instanceof String);
86          assertEquals("o incorrect", GET_METHOD_STRING, o);
87      }
88  
89      /***
90        *  test a simple method expression
91        */
92      public void testMethod()
93           throws Exception
94      {
95          /*
96           *  tests a simple method expression
97           */
98          JexlContext jc = JexlHelper.createContext();
99          jc.getVars().put("foo", new Foo() );
100         assertExpression(jc, "foo.bar()", METHOD_STRING);
101     }
102 
103     /***
104       *  test a simple method expression
105       */
106     public void testArrayAccess()
107          throws Exception
108     {
109         JexlContext jc = JexlHelper.createContext();
110 
111         /*
112          *  test List access
113          */
114 
115         List l = new ArrayList();
116         l.add(new Integer(1));
117         l.add(new Integer(2));
118         l.add(new Integer(3));
119 
120         jc.getVars().put("list", l);
121 
122         assertExpression(jc, "list[1]", new Integer(2));
123         assertExpression(jc, "list[1+1]", new Integer(3));
124         jc.getVars().put("loc", new Integer(1));
125         assertExpression(jc, "list[loc+1]", new Integer(3));
126 
127         /*
128          * test array access
129          */
130 
131         String[] args = {"hello", "there"};
132         jc.getVars().put("array", args);
133         assertExpression(jc, "array[0]", "hello");
134 
135         /*
136          * to think that this was an intentional syntax...
137          */
138         assertExpression(jc, "array.0", "hello");
139 
140         /*
141          * test map access
142          */
143         Map m = new HashMap();
144         m.put("foo", "bar");
145 
146         jc.getVars().put("map", m);
147         jc.getVars().put("key", "foo");
148 
149         assertExpression(jc, "map[\"foo\"]", "bar");
150         assertExpression(jc, "map[key]", "bar");
151 
152         /*
153          *  test bean access
154          */
155         jc.getVars().put("foo", new Foo());
156         assertExpression(jc, "foo[\"bar\"]", GET_METHOD_STRING);
157         assertExpression(jc, "foo[\"bar\"] == foo.bar", Boolean.TRUE);
158 
159     }
160 
161     public void testMulti()
162          throws Exception
163     {
164         /*
165          *  tests a simple property expression
166          */
167         JexlContext jc = JexlHelper.createContext();
168         jc.getVars().put("foo", new Foo() );
169         assertExpression(jc, "foo.innerFoo.bar()", METHOD_STRING);
170     }
171 
172     public void testBoolean()
173          throws Exception
174     {
175         JexlContext jc = JexlHelper.createContext();
176         jc.getVars().put("foo", new Foo() );
177         jc.getVars().put("a", Boolean.TRUE);
178         jc.getVars().put("b", Boolean.FALSE);
179 
180         assertExpression(jc, "foo.convertBoolean(a==b)", "Boolean : false");
181         assertExpression(jc, "foo.convertBoolean(a==true)", "Boolean : true");
182         assertExpression(jc, "foo.convertBoolean(a==false)", "Boolean : false");
183         assertExpression(jc, "foo.convertBoolean(true==false)", "Boolean : false");
184         assertExpression(jc, "true eq false", Boolean.FALSE);
185         assertExpression(jc, "true ne false", Boolean.TRUE);
186     }
187 
188     public void testStringLit()
189          throws Exception
190     {
191         /*
192          *  tests a simple property expression
193          */
194         JexlContext jc = JexlHelper.createContext();
195         jc.getVars().put("foo", new Foo() );
196         assertExpression(jc, "foo.get(\"woogie\")", "Repeat : woogie");
197     }
198 
199     public void testExpression()
200          throws Exception
201     {
202         JexlContext jc = JexlHelper.createContext();
203         jc.getVars().put("foo", new Foo() );
204         jc.getVars().put("a", Boolean.TRUE);
205         jc.getVars().put("b", Boolean.FALSE);
206         jc.getVars().put("num", new Integer(5));
207         jc.getVars().put("now", Calendar.getInstance().getTime());
208         GregorianCalendar gc = new GregorianCalendar(5000, 11, 20);
209         jc.getVars().put("now2", gc.getTime());
210 
211         assertExpression(jc, "a == b", Boolean.FALSE);
212         assertExpression(jc, "a==true", Boolean.TRUE);
213         assertExpression(jc, "a==false", Boolean.FALSE);
214         assertExpression(jc, "true==false", Boolean.FALSE);
215         
216         assertExpression(jc, "2 < 3", Boolean.TRUE);
217         assertExpression(jc, "num < 5", Boolean.FALSE);
218         assertExpression(jc, "num < num", Boolean.FALSE);
219         assertExpression(jc, "num < null", Boolean.FALSE);
220         assertExpression(jc, "num < 2.5", Boolean.FALSE);
221         assertExpression(jc, "now2 < now", Boolean.FALSE); // test comparable
222 //
223         assertExpression(jc, "'6' <= '5'", Boolean.FALSE);
224         assertExpression(jc, "num <= 5", Boolean.TRUE);
225         assertExpression(jc, "num <= num", Boolean.TRUE);
226         assertExpression(jc, "num <= null", Boolean.FALSE);
227         assertExpression(jc, "num <= 2.5", Boolean.FALSE);
228         assertExpression(jc, "now2 <= now", Boolean.FALSE); // test comparable
229 
230 //        
231         assertExpression(jc, "'6' >= '5'", Boolean.TRUE);
232         assertExpression(jc, "num >= 5", Boolean.TRUE);
233         assertExpression(jc, "num >= num", Boolean.TRUE);
234         assertExpression(jc, "num >= null", Boolean.FALSE);
235         assertExpression(jc, "num >= 2.5", Boolean.TRUE);
236         assertExpression(jc, "now2 >= now", Boolean.TRUE); // test comparable
237 
238         assertExpression(jc, "'6' > '5'", Boolean.TRUE);
239         assertExpression(jc, "num > 4", Boolean.TRUE);
240         assertExpression(jc, "num > num", Boolean.FALSE);
241         assertExpression(jc, "num > null", Boolean.FALSE);
242         assertExpression(jc, "num > 2.5", Boolean.TRUE);
243         assertExpression(jc, "now2 > now", Boolean.TRUE); // test comparable
244 
245         assertExpression(jc, "\"foo\" + \"bar\" == \"foobar\"", Boolean.TRUE);
246 
247     }
248 
249     public void testEmpty()
250          throws Exception
251     {
252         JexlContext jc = JexlHelper.createContext();
253         jc.getVars().put("string", "");
254         jc.getVars().put("array", new Object[0]);
255         jc.getVars().put("map", new HashMap());
256         jc.getVars().put("list", new ArrayList());
257         jc.getVars().put("set", (new HashMap()).keySet());
258         jc.getVars().put("longstring", "thingthing");
259 
260         /*
261          *  I can't believe anyone thinks this is a syntax.. :)
262          */
263         assertExpression(jc, "empty nullthing", Boolean.TRUE);
264         assertExpression(jc, "empty string", Boolean.TRUE);
265         assertExpression(jc, "empty array", Boolean.TRUE);
266         assertExpression(jc, "empty map", Boolean.TRUE);
267         assertExpression(jc, "empty set", Boolean.TRUE);
268         assertExpression(jc, "empty list", Boolean.TRUE);
269         assertExpression(jc, "empty longstring", Boolean.FALSE);
270         assertExpression(jc, "not empty longstring", Boolean.TRUE);
271     }
272 
273     public void testSize()
274          throws Exception
275     {
276         JexlContext jc = JexlHelper.createContext();
277         jc.getVars().put("s", "five!");
278         jc.getVars().put("array", new Object[5]);
279 
280         Map map = new HashMap();
281 
282         map.put("1", new Integer(1));
283         map.put("2", new Integer(2));
284         map.put("3", new Integer(3));
285         map.put("4", new Integer(4));
286         map.put("5", new Integer(5));
287 
288         jc.getVars().put("map", map);
289 
290         List list = new ArrayList();
291 
292         list.add("1");
293         list.add("2");
294         list.add("3");
295         list.add("4");
296         list.add("5");
297 
298         jc.getVars().put("list", list);
299 
300         // 30652 - support for set
301         Set set = new HashSet();
302         set.addAll(list);
303         set.add("1");
304         
305         jc.getVars().put("set", set);
306         
307         // support generic int size() method
308         BitSet bitset = new BitSet(5);
309         jc.getVars().put("bitset", bitset);
310 
311         assertExpression(jc, "size(s)", new Integer(5));
312         assertExpression(jc, "size(array)", new Integer(5));
313         assertExpression(jc, "size(list)", new Integer(5));
314         assertExpression(jc, "size(map)", new Integer(5));
315         assertExpression(jc, "size(set)", new Integer(5));
316         assertExpression(jc, "size(bitset)", new Integer(64));
317         assertExpression(jc, "list.size()", new Integer(5));
318         assertExpression(jc, "map.size()", new Integer(5));
319         assertExpression(jc, "set.size()", new Integer(5));
320         assertExpression(jc, "bitset.size()", new Integer(64));
321 
322         assertExpression(jc, "list.get(size(list) - 1)", "5");
323         assertExpression(jc, "list[size(list) - 1]", "5");
324         assertExpression(jc, "list.get(list.size() - 1)", "5");
325     }
326 
327     public void testSizeAsProperty() throws Exception
328     {
329         JexlContext jc = JexlHelper.createContext();
330 
331         jc.getVars().put("map", Collections.singletonMap( "size", "cheese"));
332         jc.getVars().put("foo", new Foo());
333 
334         assertExpression(jc, "map['size']", "cheese");
335 // PR - unsure whether or not we should support map.size or force usage of the above 'escaped' version        
336 //        assertExpression(jc, "map.size", "cheese");
337         assertExpression(jc, "foo.getSize()", new Integer(22));
338         // failing assertion for size property
339         //assertExpression(jc, "foo.size", new Integer(22));
340     }
341 
342     /***
343       *  test some String method calls
344       */
345     public void testStringMethods()
346          throws Exception
347     {
348         JexlContext jc = JexlHelper.createContext();
349 
350         jc.getVars().put("foo", "abcdef");
351 
352         assertExpression(jc, "foo.substring(3)", "def");
353         assertExpression(jc, "foo.substring(0,(size(foo)-3))", "abc");
354         assertExpression(jc, "foo.substring(0,size(foo)-3)", "abc");
355         assertExpression(jc, "foo.substring(0,foo.length()-3)", "abc");
356         assertExpression(jc, "foo.substring(0, 1+1)", "ab");
357     }
358 
359 
360 
361    /***
362     *  test some simple mathematical calculations
363     */
364    public void testUnaryMinus()
365         throws Exception
366    {
367        JexlContext jc = JexlHelper.createContext();
368 
369        jc.getVars().put("aByte", new Byte((byte)1));
370        jc.getVars().put("aShort", new Short((short)2));
371        jc.getVars().put("anInteger", new Integer(3));
372        jc.getVars().put("aLong", new Long(4));
373        jc.getVars().put("aFloat", new Float(5.5));
374        jc.getVars().put("aDouble", new Double(6.6));
375        jc.getVars().put("aBigInteger", new BigInteger("7"));
376        jc.getVars().put("aBigDecimal", new BigDecimal("8.8"));
377        assertExpression(jc, "-3", new Integer("-3"));
378        assertExpression(jc, "-3.0", new Float("-3.0"));
379        assertExpression(jc, "-aByte", new Byte((byte)-1));
380        assertExpression(jc, "-aShort", new Short((short)-2));
381        assertExpression(jc, "-anInteger", new Integer(-3));
382        assertExpression(jc, "-aLong", new Long(-4));
383        assertExpression(jc, "-aFloat", new Float(-5.5));
384        assertExpression(jc, "-aDouble", new Double(-6.6));
385        assertExpression(jc, "-aBigInteger", new BigInteger("-7"));
386        assertExpression(jc, "-aBigDecimal", new BigDecimal("-8.8"));
387    }
388 
389     
390     /***
391       *  test some simple mathematical calculations
392       */
393     public void testCalculations()
394          throws Exception
395     {
396         JexlContext jc = JexlHelper.createContext();
397 
398         jc.getVars().put("foo", new Integer(2) );
399 
400         assertExpression(jc, "foo + 2", new Long(4));
401         assertExpression(jc, "3 + 3", new Long(6));
402         assertExpression(jc, "3 + 3 + foo", new Long(8));
403         assertExpression(jc, "3 * 3", new Long(9));
404         assertExpression(jc, "3 * 3 + foo", new Long(11));
405         assertExpression(jc, "3 * 3 - foo", new Long(7));
406 
407         /*
408          * test some floaty stuff
409          */
410         assertExpression(jc, "3 * \"3.0\"", new Double(9));
411         assertExpression(jc, "3 * 3.0", new Double(9));
412 
413         /*
414          *  test / and %
415          */
416         assertExpression(jc, "6 / 3", new Double(6/3));
417         assertExpression(jc, "6.4 / 3", new Double(6.4 / 3));
418         assertExpression(jc, "0 / 3", new Double(0 / 3));
419         assertExpression(jc, "3 / 0", new Double(0));
420         assertExpression(jc, "4 % 3", new Long(1));
421         assertExpression(jc, "4.8 % 3", new Double(4.8 % 3));
422 
423         /*
424          * test to ensure new string cat works
425          */
426         jc.getVars().put("stringy", "thingy" );
427         assertExpression(jc, "stringy + 2", "thingy2");
428 
429         /*
430          * test new null coersion
431          */
432         jc.getVars().put("imanull", null );
433         assertExpression(jc, "imanull + 2", new Long(2));
434         assertExpression(jc, "imanull + imanull", new Long(0));
435         
436         /* test for bugzilla 31577 */
437         jc.getVars().put("n", new Integer(0));
438         assertExpression(jc, "n != null && n != 0", Boolean.FALSE);
439     }
440 
441     /***
442       *  test some simple conditions
443       */
444     public void testConditions()
445          throws Exception
446     {
447         JexlContext jc = JexlHelper.createContext();
448         jc.getVars().put("foo", new Integer(2) );
449         jc.getVars().put("aFloat", new Float(1));
450         jc.getVars().put("aDouble", new Double(2));
451         jc.getVars().put("aChar", new Character('A'));
452         jc.getVars().put("aBool", Boolean.TRUE);
453         StringBuffer buffer = new StringBuffer("abc");
454         List list = new ArrayList();
455         List list2 = new LinkedList();
456         jc.getVars().put("aBuffer", buffer);
457         jc.getVars().put("aList", list);
458         jc.getVars().put("bList", list2);
459         
460         assertExpression(jc, "foo == 2", Boolean.TRUE);
461         assertExpression(jc, "2 == 3", Boolean.FALSE);
462         assertExpression(jc, "3 == foo", Boolean.FALSE);
463         assertExpression(jc, "3 != foo", Boolean.TRUE);
464         assertExpression(jc, "foo != 2", Boolean.FALSE);
465         // test float and double equality
466         assertExpression(jc, "aFloat eq aDouble", Boolean.FALSE);
467         assertExpression(jc, "aFloat ne aDouble", Boolean.TRUE);
468         assertExpression(jc, "aFloat == aDouble", Boolean.FALSE);
469         assertExpression(jc, "aFloat != aDouble", Boolean.TRUE);
470         // test number and character equality
471         assertExpression(jc, "foo == aChar", Boolean.FALSE);
472         assertExpression(jc, "foo != aChar", Boolean.TRUE);
473         // test string and boolean
474         assertExpression(jc, "aBool == 'true'", Boolean.TRUE);
475         assertExpression(jc, "aBool == 'false'", Boolean.FALSE);
476         assertExpression(jc, "aBool != 'false'", Boolean.TRUE);
477         // test null and boolean
478         assertExpression(jc, "aBool == notThere", Boolean.FALSE);
479         assertExpression(jc, "aBool != notThere", Boolean.TRUE);
480         // anything and string as a string comparison
481         assertExpression(jc, "aBuffer == 'abc'", Boolean.TRUE);
482         assertExpression(jc, "aBuffer != 'abc'", Boolean.FALSE);
483         // arbitrary equals
484         assertExpression(jc, "aList == bList", Boolean.TRUE);
485         assertExpression(jc, "aList != bList", Boolean.FALSE);
486     }
487 
488     /***
489       *  test some simple conditions
490       */
491     public void testNotConditions()
492          throws Exception
493     {
494         JexlContext jc = JexlHelper.createContext();
495 
496         Foo foo = new Foo();
497         jc.getVars().put("x", Boolean.TRUE );
498         jc.getVars().put("foo", foo );
499         jc.getVars().put("bar", "true" );
500 
501         assertExpression(jc, "!x", Boolean.FALSE);
502         assertExpression(jc, "x", Boolean.TRUE);
503         assertExpression(jc, "!bar", Boolean.FALSE);
504         assertExpression(jc, "!foo.isSimple()", Boolean.FALSE);
505         assertExpression(jc, "foo.isSimple()", Boolean.TRUE);
506         assertExpression(jc, "!foo.simple", Boolean.FALSE);
507         assertExpression(jc, "foo.simple", Boolean.TRUE);
508         assertExpression(jc, "foo.getCheeseList().size() == 3", Boolean.TRUE);
509         assertExpression(jc, "foo.cheeseList.size() == 3", Boolean.TRUE);
510 
511         jc.getVars().put("string", "");
512         assertExpression(jc, "not empty string", Boolean.FALSE);
513         assertExpression(jc, "not(empty string)", Boolean.FALSE);
514         assertExpression(jc, "not empty(string)", Boolean.FALSE);
515         assertExpression(jc, "! empty string", Boolean.FALSE);
516         assertExpression(jc, "!(empty string)", Boolean.FALSE);
517         assertExpression(jc, "!empty(string)", Boolean.FALSE);
518 
519     }
520 
521 
522     /***
523       *  GMJ : disabled - need to fix
524       *
525       *  test some simple conditions
526       */
527     public void testNotConditionsWithDots()
528          throws Exception
529     {
530         JexlContext jc = JexlHelper.createContext();
531 
532         jc.getVars().put("x.a", Boolean.TRUE );
533         jc.getVars().put("x.b", Boolean.FALSE );
534 
535         assertExpression(jc, "x.a", Boolean.TRUE);
536         assertExpression(jc, "!x.a", Boolean.FALSE);
537         assertExpression(jc, "!x.b", Boolean.TRUE);
538     }
539 
540     /***
541       *  test some simple conditions
542       */
543     public void testComparisons()
544          throws Exception
545     {
546         JexlContext jc = JexlHelper.createContext();
547         jc.getVars().put("foo", "the quick and lazy fox" );
548 
549         assertExpression(jc, "foo.indexOf('quick') > 0", Boolean.TRUE);
550         assertExpression(jc, "foo.indexOf('bar') >= 0", Boolean.FALSE);
551         assertExpression(jc, "foo.indexOf('bar') < 0", Boolean.TRUE);
552     }
553 
554     /***
555       *  test some null conditions
556       */
557     public void testNull()
558          throws Exception
559     {
560         JexlContext jc = JexlHelper.createContext();
561         jc.getVars().put("bar", new Integer(2) );
562 
563         assertExpression(jc, "empty foo", Boolean.TRUE);
564         assertExpression(jc, "bar == null", Boolean.FALSE);
565         assertExpression(jc, "foo == null", Boolean.TRUE);
566         assertExpression(jc, "bar != null", Boolean.TRUE);
567         assertExpression(jc, "foo != null", Boolean.FALSE);
568         assertExpression(jc, "empty(bar)", Boolean.FALSE);
569         assertExpression(jc, "empty(foo)", Boolean.TRUE);
570     }
571 
572     /***
573       *  test some blank strings
574       */
575     public void testBlankStrings()
576          throws Exception
577     {
578         JexlContext jc = JexlHelper.createContext();
579         jc.getVars().put("bar", "" );
580 
581         assertExpression(jc, "foo == ''", Boolean.FALSE);
582         assertExpression(jc, "bar == ''", Boolean.TRUE);
583         assertExpression(jc, "barnotexist == ''", Boolean.FALSE);
584         assertExpression(jc, "empty bar", Boolean.TRUE);
585         assertExpression(jc, "bar.length() == 0", Boolean.TRUE);
586         assertExpression(jc, "size(bar) == 0", Boolean.TRUE);
587     }
588 
589     /***
590       *  test some blank strings
591       */
592     public void testLogicExpressions()
593          throws Exception
594     {
595         JexlContext jc = JexlHelper.createContext();
596         jc.getVars().put("foo", "abc" );
597         jc.getVars().put("bar", "def" );
598 
599         assertExpression(jc, "foo == 'abc' || bar == 'abc'", Boolean.TRUE);
600         assertExpression(jc, "foo == 'abc' or bar == 'abc'", Boolean.TRUE);
601         assertExpression(jc, "foo == 'abc' && bar == 'abc'", Boolean.FALSE);
602         assertExpression(jc, "foo == 'abc' and bar == 'abc'", Boolean.FALSE);
603 
604         assertExpression(jc, "foo == 'def' || bar == 'abc'", Boolean.FALSE);
605         assertExpression(jc, "foo == 'def' or bar == 'abc'", Boolean.FALSE);
606         assertExpression(jc, "foo == 'abc' && bar == 'def'", Boolean.TRUE);
607         assertExpression(jc, "foo == 'abc' and bar == 'def'", Boolean.TRUE);
608     }
609 
610 
611     /***
612       *  test some simple double array lookups
613       */
614     public void testDoubleArrays()
615          throws Exception
616     {
617         JexlContext jc = JexlHelper.createContext();
618 
619         Object[][] foo = new Object[2][2];
620         foo[0][0] = "one";
621         foo[0][1] = "two";
622 
623         jc.getVars().put("foo", foo );
624 
625         assertExpression(jc, "foo[0][1]", "two");
626     }
627 
628     /***
629       *  test variables with underscore names
630       */
631     public void testVariableNames()
632          throws Exception
633     {
634         JexlContext jc = JexlHelper.createContext();
635         jc.getVars().put("foo_bar", "123" );
636         
637         assertExpression(jc, "foo_bar", "123");
638     }
639 
640     /***
641       *  test the use of dot notation to lookup map entries
642       */
643     public void testMapDot()
644          throws Exception
645     {
646         Map foo = new HashMap();
647         foo.put( "bar", "123" );
648 
649         JexlContext jc = JexlHelper.createContext();
650         jc.getVars().put("foo", foo );
651         
652         assertExpression(jc, "foo.bar", "123");
653     }
654 
655     /***
656      *  Tests string literals
657      */
658     public void testStringLiterals()
659         throws Exception
660     {
661         JexlContext jc = JexlHelper.createContext();
662         jc.getVars().put("foo", "bar" );
663 
664         assertExpression(jc, "foo == \"bar\"", Boolean.TRUE);
665         assertExpression(jc, "foo == 'bar'", Boolean.TRUE);
666     }
667 
668     /***
669       *  test the use of an int based property
670       */
671     public void testIntProperty()
672          throws Exception
673     {
674         Foo foo = new Foo();
675 
676         // lets check the square function first..
677         assertEquals(4, foo.square(2));
678         assertEquals(4, foo.square(-2));
679 
680         JexlContext jc = JexlHelper.createContext();
681         jc.getVars().put("foo", foo );
682 
683         assertExpression(jc, "foo.count", new Integer(5));
684         assertExpression(jc, "foo.square(2)", new Integer(4));
685         assertExpression(jc, "foo.square(-2)", new Integer(4));
686     }
687 
688     /***
689       *  test the -1 comparison bug
690       */
691     public void testNegativeIntComparison()
692          throws Exception
693     {
694         JexlContext jc = JexlHelper.createContext();
695         Foo foo = new Foo();
696         jc.getVars().put("foo", foo );
697 
698         assertExpression(jc, "foo.count != -1", Boolean.TRUE);
699         assertExpression(jc, "foo.count == 5", Boolean.TRUE);
700         assertExpression(jc, "foo.count == -1", Boolean.FALSE);
701     }
702 
703     public void testArrayProperty()
704         throws Exception
705     {
706         Foo foo = new Foo();
707 
708         JexlContext jc = JexlHelper.createContext();
709         jc.getVars().put("foo", foo );
710 
711         assertExpression(jc, "foo.array[1]", GET_METHOD_ARRAY[1]);
712         assertExpression(jc, "foo.array.1", GET_METHOD_ARRAY[1]);
713         assertExpression(jc, "foo.array2[1][1]", GET_METHOD_ARRAY2[1][1]);
714         //assertExpression(jc, "foo.array2.1.1", GET_METHOD_ARRAY2[1][1]);
715     }
716 
717     /***
718      * Attempts to recreate bug http://jira.werken.com/ViewIssue.jspa?key=JELLY-8
719      */
720     public void testCharAtBug()
721         throws Exception
722     {
723         JexlContext jc = JexlHelper.createContext();
724 
725         jc.getVars().put("foo", "abcdef");
726 
727         assertExpression(jc, "foo.substring(2,4)", "cd");
728         assertExpression(jc, "foo.charAt(2)", new Character('c'));
729 
730         try {
731             assertExpression(jc, "foo.charAt(-2)", null);
732             fail("this test should have thrown an exception" );
733         }
734         catch (IndexOutOfBoundsException e) {
735             // expected behaviour
736         }
737         catch (Exception e) {
738             throw e;
739         }
740     }
741 
742     public void testEmptyDottedVariableName() throws Exception
743     {
744         JexlContext jc = JexlHelper.createContext();
745 
746         jc.getVars().put( "this.is.a.test", "");
747 
748         assertExpression(jc, "empty(this.is.a.test)", Boolean.TRUE);
749     }
750 
751     public void testEmptySubListOfMap() throws Exception
752     {
753         JexlContext jc = JexlHelper.createContext();
754         Map m = Collections.singletonMap("aList", Collections.EMPTY_LIST);
755 
756         jc.getVars().put( "aMap", m );
757 
758         assertExpression( jc, "empty( aMap.aList )", Boolean.TRUE );
759     }
760 
761     public void testCoercionWithComparisionOperators()
762         throws Exception
763     {
764         JexlContext jc = JexlHelper.createContext();
765 
766         assertExpression(jc, "'2' > 1", Boolean.TRUE);
767         assertExpression(jc, "'2' >= 1", Boolean.TRUE);
768         assertExpression(jc, "'2' >= 2", Boolean.TRUE);
769         assertExpression(jc, "'2' < 1", Boolean.FALSE);
770         assertExpression(jc, "'2' <= 1", Boolean.FALSE);
771         assertExpression(jc, "'2' <= 2", Boolean.TRUE);
772 
773         assertExpression(jc, "2 > '1'", Boolean.TRUE);
774         assertExpression(jc, "2 >= '1'", Boolean.TRUE);
775         assertExpression(jc, "2 >= '2'", Boolean.TRUE);
776         assertExpression(jc, "2 < '1'", Boolean.FALSE);
777         assertExpression(jc, "2 <= '1'", Boolean.FALSE);
778         assertExpression(jc, "2 <= '2'", Boolean.TRUE);
779 }
780 
781     public void testResolver()
782         throws Exception
783     {
784         /*
785          * first, a simple override
786          */
787 
788         Expression expr = ExpressionFactory.createExpression("foo.bar");
789         expr.addPreResolver(new FlatResolver());
790 
791         JexlContext jc = JexlHelper.createContext();
792         Foo foo = new Foo();
793         jc.getVars().put("foo.bar", "flat value");
794         jc.getVars().put("foo", foo );
795 
796         Object o = expr.evaluate(jc);
797         assertEquals("flat override", "flat value", o);
798 
799         /*
800          * now, let the resolver not find it and have it drop to jexl
801          */
802         expr = ExpressionFactory.createExpression("foo.bar.length()");
803         expr.addPreResolver(new FlatResolver());
804         o = expr.evaluate(jc);
805         assertEquals("flat override 1", new Integer(GET_METHOD_STRING.length()), o);
806 
807         /*
808          * now, let the resolver not find it and NOT drop to jexl
809          */
810 
811         expr = ExpressionFactory.createExpression("foo.bar.length()");
812         expr.addPreResolver(new FlatResolver(false));
813         o = expr.evaluate(jc);
814         assertEquals("flat override 2", o, null);
815         
816         // now for a post resolver
817         expr = ExpressionFactory.createExpression("foo.bar.baz");
818         Long result = new Long(1);
819         jc.getVars().put("foo.bar.baz", result);
820         expr.addPostResolver(new FlatResolver());
821         assertEquals("flat override", result, expr.evaluate(jc));
822 
823     }
824 
825     /***
826      * Test that 'and' only evaluates the second item if needed
827      * @throws Exception if there are errors
828      */
829     public void testBooleanShortCircuitAnd() throws Exception
830     {
831         // handle false for the left arg of 'and'
832         Foo tester = new Foo();
833         JexlContext jc = JexlHelper.createContext();
834         jc.getVars().put("first", Boolean.FALSE);
835         jc.getVars().put("foo", tester);
836         Expression expr = ExpressionFactory.createExpression("first and foo.trueAndModify");
837         expr.evaluate(jc);
838         assertTrue("Short circuit failure: rhs evaluated when lhs FALSE", !tester.getModified());
839         // handle true for the left arg of 'and' 
840         tester = new Foo();
841         jc.getVars().put("first", Boolean.TRUE);
842         jc.getVars().put("foo", tester);
843         expr.evaluate(jc);
844         assertTrue("Short circuit failure: rhs not evaluated when lhs TRUE", tester.getModified());
845     }
846     
847     /***
848      * Test that 'or' only evaluates the second item if needed
849      * @throws Exception if there are errors
850      */
851     public void testBooleanShortCircuitOr() throws Exception
852     {
853         // handle false for the left arg of 'or'
854         Foo tester = new Foo();
855         JexlContext jc = JexlHelper.createContext();
856         jc.getVars().put("first", Boolean.FALSE);
857         jc.getVars().put("foo", tester);
858         Expression expr = ExpressionFactory.createExpression("first or foo.trueAndModify");
859         expr.evaluate(jc);
860         assertTrue("Short circuit failure: rhs not evaluated when lhs FALSE", tester.getModified());
861         // handle true for the left arg of 'or' 
862         tester = new Foo();
863         jc.getVars().put("first", Boolean.TRUE);
864         jc.getVars().put("foo", tester);
865         expr.evaluate(jc);
866         assertTrue("Short circuit failure: rhs evaluated when lhs TRUE", !tester.getModified());
867     }
868 
869     /***
870      * Simple test of '+' as a string concatenation operator
871      * @throws Exception
872      */
873     public void testStringConcatenation() throws Exception
874     {
875         JexlContext jc = JexlHelper.createContext();
876         jc.getVars().put("first", "Hello");
877         jc.getVars().put("second", "World");
878         assertExpression(jc, "first + ' ' + second", "Hello World");
879     }
880 
881     /***
882      * Ensures static methods on objects can be called.
883      */
884     public void testStaticMethodInvocation() throws Exception
885     {
886         JexlContext jc = JexlHelper.createContext();
887         jc.getVars().put("aBool", Boolean.FALSE);
888         assertExpression(jc, "aBool.valueOf('true')", Boolean.TRUE);
889     }
890     
891     public void testStaticMethodInvocationOnClasses() throws Exception
892     {
893         JexlContext jc = JexlHelper.createContext();
894         jc.getVars().put("Boolean", Boolean.class);
895         assertExpression(jc, "Boolean.valueOf('true')", Boolean.TRUE);        
896     }
897     
898     public void testToString() throws Exception {
899         String code = "abcd";
900         Expression expr = ExpressionFactory.createExpression(code);
901         assertEquals("Bad expression value", code, expr.toString());
902     }
903     
904     /***
905      * Make sure bad syntax throws ParseException
906      * @throws Exception on errors
907      */
908     public void testBadParse() throws Exception
909     {
910         try
911         {
912             assertExpression(JexlHelper.createContext(), "empty()", null);
913             fail("Bad expression didn't throw ParseException");
914         }
915         catch (ParseException pe)
916         {
917             // expected behaviour
918         }
919     }
920 
921     /***
922      * Test the ## comment in a string
923      * @throws Exception
924      */
925     public void testComment() throws Exception
926     {
927         assertExpression(JexlHelper.createContext(), "## double or nothing\n 1 + 1", Long.valueOf("2"));
928     }
929     
930     /***
931      * Test assignment.
932      * @throws Exception
933      */
934     public void testAssignment() throws Exception
935     {
936         JexlContext jc = JexlHelper.createContext();
937         jc.getVars().put("aString", "Hello");
938         Foo foo = new Foo();
939         jc.getVars().put("foo", foo);
940         Parser parser = new Parser(new StringReader(";"));
941         parser.parse(new StringReader("aString = 'World';"));
942         
943         assertExpression(jc, "hello = 'world'", "world");
944         assertEquals("hello variable not changed", "world", jc.getVars().get("hello"));
945         assertExpression(jc, "result = 1 + 1", new Long(2));
946         assertEquals("result variable not changed", new Long(2), jc.getVars().get("result"));
947         // todo: make sure properties can be assigned to, fall back to flat var if no property
948         // assertExpression(jc, "foo.property1 = '99'", "99");
949         // assertEquals("property not set", "99", foo.getProperty1());
950     }
951     
952     public void testAntPropertiesWithMethods() throws Exception
953     {
954         JexlContext jc = JexlHelper.createContext();
955         String value = "Stinky Cheese";
956         jc.getVars().put("maven.bob.food", value);
957         assertExpression(jc, "maven.bob.food.length()", new Integer(value.length()));
958         assertExpression(jc, "empty(maven.bob.food)", Boolean.FALSE);
959         assertExpression(jc, "size(maven.bob.food)", new Integer(value.length()));
960         assertExpression(jc, "maven.bob.food + ' is good'", value + " is good");
961 
962         // DG: Note the following ant properties don't work
963 //        String version = "1.0.3";
964 //        jc.getVars().put("commons-logging", version);
965 //        assertExpression(jc, "commons-logging", version);
966     }
967 
968     /***
969      * Asserts that the given expression returns the given value when applied to the
970      * given context
971      */
972     protected void assertExpression(JexlContext jc, String expression, Object expected) throws Exception
973     {
974         Expression e = ExpressionFactory.createExpression(expression);
975         Object actual = e.evaluate(jc);
976         assertEquals(expression, expected, actual);
977     }
978 
979 
980     /***
981      *  Helps in debugging the testcases when working with it
982      *
983      */
984     public static void main(String[] args)
985         throws Exception
986     {
987         JexlTest jt = new JexlTest("foo");
988         jt.testEmpty();
989     }
990 
991 }