    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 }