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