| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.jexl3; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.SortedSet; |
| import java.util.TreeSet; |
| import org.apache.commons.jexl3.junit.Asserter; |
| import java.io.StringWriter; |
| import java.text.DecimalFormat; |
| import java.text.SimpleDateFormat; |
| import java.util.Arrays; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.Locale; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Tests for the startsWith, endsWith, match and range operators. |
| * @since 3.0 |
| */ |
| @SuppressWarnings({"UnnecessaryBoxing", "AssertEqualsBetweenInconvertibleTypes"}) |
| public class ArithmeticOperatorTest extends JexlTestCase { |
| private Asserter asserter; |
| |
| @Before |
| @Override |
| public void setUp() { |
| asserter = new Asserter(JEXL); |
| asserter.setStrict(false); |
| } |
| |
| /** |
| * Create the named test. |
| * @param name test name |
| */ |
| public ArithmeticOperatorTest() { |
| super("ArithmeticOperatorTest"); |
| } |
| |
| @Test |
| public void testRegexp() throws Exception { |
| asserter.setVariable("str", "abc456"); |
| asserter.assertExpression("str =~ '.*456'", Boolean.TRUE); |
| asserter.assertExpression("str !~ 'ABC.*'", Boolean.TRUE); |
| asserter.setVariable("match", "abc.*"); |
| asserter.setVariable("nomatch", ".*123"); |
| asserter.assertExpression("str =~ match", Boolean.TRUE); |
| asserter.assertExpression("str !~ match", Boolean.FALSE); |
| asserter.assertExpression("str !~ nomatch", Boolean.TRUE); |
| asserter.assertExpression("str =~ nomatch", Boolean.FALSE); |
| asserter.setVariable("match", java.util.regex.Pattern.compile("abc.*")); |
| asserter.setVariable("nomatch", java.util.regex.Pattern.compile(".*123")); |
| asserter.assertExpression("str =~ match", Boolean.TRUE); |
| asserter.assertExpression("str !~ match", Boolean.FALSE); |
| asserter.assertExpression("str !~ nomatch", Boolean.TRUE); |
| asserter.assertExpression("str =~ nomatch", Boolean.FALSE); |
| // check the in/not-in variant |
| asserter.assertExpression("'a' =~ ['a','b','c','d','e','f']", Boolean.TRUE); |
| asserter.assertExpression("'a' !~ ['a','b','c','d','e','f']", Boolean.FALSE); |
| asserter.assertExpression("'z' =~ ['a','b','c','d','e','f']", Boolean.FALSE); |
| asserter.assertExpression("'z' !~ ['a','b','c','d','e','f']", Boolean.TRUE); |
| } |
| |
| @Test |
| public void testStartsEndsWithString() throws Exception { |
| asserter.setVariable("x", "foobar"); |
| asserter.assertExpression("x =^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x =$ 'foo'", Boolean.FALSE); |
| asserter.setVariable("x", "barfoo"); |
| asserter.assertExpression("x =^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x =$ 'foo'", Boolean.TRUE); |
| } |
| |
| @Test |
| public void testStartsEndsWithStringDot() throws Exception { |
| asserter.setVariable("x.y", "foobar"); |
| asserter.assertExpression("x.y =^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x.y =$ 'foo'", Boolean.FALSE); |
| asserter.setVariable("x.y", "barfoo"); |
| asserter.assertExpression("x.y =^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x.y =$ 'foo'", Boolean.TRUE); |
| } |
| |
| @Test |
| public void testNotStartsEndsWithString() throws Exception { |
| asserter.setVariable("x", "foobar"); |
| asserter.assertExpression("x !^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x !$ 'foo'", Boolean.TRUE); |
| asserter.setVariable("x", "barfoo"); |
| asserter.assertExpression("x !^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x !$ 'foo'", Boolean.FALSE); |
| } |
| |
| @Test |
| public void testNotStartsEndsWithStringDot() throws Exception { |
| asserter.setVariable("x.y", "foobar"); |
| asserter.assertExpression("x.y !^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x.y !$ 'foo'", Boolean.TRUE); |
| asserter.setVariable("x.y", "barfoo"); |
| asserter.assertExpression("x.y !^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x.y !$ 'foo'", Boolean.FALSE); |
| } |
| |
| public static class MatchingContainer { |
| private final Set<Integer> values; |
| |
| public MatchingContainer(int[] is) { |
| values = new HashSet<Integer>(); |
| for (int value : is) { |
| values.add(value); |
| } |
| } |
| |
| public boolean contains(int value) { |
| return values.contains(value); |
| } |
| } |
| |
| public static class IterableContainer implements Iterable<Integer> { |
| private final SortedSet<Integer> values; |
| |
| public IterableContainer(int[] is) { |
| values = new TreeSet<Integer>(); |
| for (int value : is) { |
| values.add(value); |
| } |
| } |
| |
| @Override |
| public Iterator<Integer> iterator() { |
| return values.iterator(); |
| } |
| |
| public boolean contains(int i) { |
| return values.contains(i); |
| } |
| |
| public boolean contains(int[] i) { |
| return values.containsAll(Arrays.asList(i)); |
| } |
| |
| public boolean startsWith(int i) { |
| return values.first().equals(i); |
| } |
| |
| public boolean endsWith(int i) { |
| return values.last().equals(i); |
| } |
| |
| public boolean startsWith(int[] i) { |
| SortedSet<Integer> sw = values.headSet(i.length); |
| int n = 0; |
| for(Integer value : sw) { |
| if(!value.equals(i[n++])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| public boolean endsWith(int[] i) { |
| SortedSet<Integer> sw = values.tailSet(values.size() - i.length); |
| int n = 0; |
| for(Integer value : sw) { |
| if(!value.equals(i[n++])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| @Test |
| public void testMatch() throws Exception { |
| // check in/not-in on array, list, map, set and duck-type collection |
| int[] ai = {2, 4, 42, 54}; |
| List<Integer> al = new ArrayList<Integer>(); |
| for (int i : ai) { |
| al.add(i); |
| } |
| Map<Integer, String> am = new HashMap<Integer, String>(); |
| am.put(2, "two"); |
| am.put(4, "four"); |
| am.put(42, "forty-two"); |
| am.put(54, "fifty-four"); |
| MatchingContainer ad = new MatchingContainer(ai); |
| IterableContainer ic = new IterableContainer(ai); |
| Set<Integer> as = ad.values; |
| Object[] vars = {ai, al, am, ad, as, ic}; |
| |
| for (Object var : vars) { |
| asserter.setVariable("container", var); |
| for (int x : ai) { |
| asserter.setVariable("x", x); |
| asserter.assertExpression("x =~ container", Boolean.TRUE); |
| } |
| asserter.setVariable("x", 169); |
| asserter.assertExpression("x !~ container", Boolean.TRUE); |
| } |
| } |
| |
| @Test |
| public void testStartsEndsWith() throws Exception { |
| asserter.setVariable("x", "foobar"); |
| asserter.assertExpression("x =^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x =$ 'foo'", Boolean.FALSE); |
| asserter.setVariable("x", "barfoo"); |
| asserter.assertExpression("x =^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x =$ 'foo'", Boolean.TRUE); |
| |
| int[] ai = {2, 4, 42, 54}; |
| IterableContainer ic = new IterableContainer(ai); |
| asserter.setVariable("x", ic); |
| asserter.assertExpression("x =^ 2", Boolean.TRUE); |
| asserter.assertExpression("x =$ 54", Boolean.TRUE); |
| asserter.assertExpression("x =^ 4", Boolean.FALSE); |
| asserter.assertExpression("x =$ 42", Boolean.FALSE); |
| asserter.assertExpression("x =^ [2, 4]", Boolean.TRUE); |
| asserter.assertExpression("x =^ [42, 54]", Boolean.TRUE); |
| } |
| |
| @Test |
| public void testNotStartsEndsWith() throws Exception { |
| asserter.setVariable("x", "foobar"); |
| asserter.assertExpression("x !^ 'foo'", Boolean.FALSE); |
| asserter.assertExpression("x !$ 'foo'", Boolean.TRUE); |
| asserter.setVariable("x", "barfoo"); |
| asserter.assertExpression("x !^ 'foo'", Boolean.TRUE); |
| asserter.assertExpression("x !$ 'foo'", Boolean.FALSE); |
| |
| int[] ai = {2, 4, 42, 54}; |
| IterableContainer ic = new IterableContainer(ai); |
| asserter.setVariable("x", ic); |
| asserter.assertExpression("x !^ 2", Boolean.FALSE); |
| asserter.assertExpression("x !$ 54", Boolean.FALSE); |
| asserter.assertExpression("x !^ 4", Boolean.TRUE); |
| asserter.assertExpression("x !$ 42", Boolean.TRUE); |
| asserter.assertExpression("x !^ [2, 4]", Boolean.FALSE); |
| asserter.assertExpression("x !^ [42, 54]", Boolean.FALSE); |
| } |
| |
| public static class Aggregate { |
| private Aggregate() {} |
| public static int sum(Iterable<Integer> ii) { |
| int sum = 0; |
| for(Integer i : ii) { |
| sum += i; |
| } |
| return sum; |
| } |
| } |
| |
| @Test |
| @SuppressWarnings("unchecked") |
| public void testInterval() throws Exception { |
| Map<String, Object> ns = new HashMap<String, Object>(); |
| ns.put("calc", Aggregate.class); |
| JexlEngine jexl = new JexlBuilder().namespaces(ns).create(); |
| JexlScript script; |
| Object result; |
| |
| script = jexl.createScript("1 .. 3"); |
| result = script.execute(null); |
| Assert.assertTrue(result instanceof Iterable<?>); |
| Iterator<Integer> ii = ((Iterable<Integer>) result).iterator(); |
| Assert.assertEquals(Integer.valueOf(1), ii.next()); |
| Assert.assertEquals(Integer.valueOf(2), ii.next()); |
| Assert.assertEquals(Integer.valueOf(3), ii.next()); |
| |
| script = jexl.createScript("(4 - 3) .. (9 / 3)"); |
| result = script.execute(null); |
| Assert.assertTrue(result instanceof Iterable<?>); |
| ii = ((Iterable<Integer>) result).iterator(); |
| Assert.assertEquals(Integer.valueOf(1), ii.next()); |
| Assert.assertEquals(Integer.valueOf(2), ii.next()); |
| Assert.assertEquals(Integer.valueOf(3), ii.next()); |
| |
| // sum of 1, 2, 3 |
| script = jexl.createScript("var x = 0; for(var y : ((5 - 4) .. (12 / 4))) { x = x + y }; x"); |
| result = script.execute(null); |
| Assert.assertEquals(Integer.valueOf(6), result); |
| |
| script = jexl.createScript("calc:sum(1 .. 3)"); |
| result = script.execute(null); |
| Assert.assertEquals(Integer.valueOf(6), result); |
| |
| script = jexl.createScript("calc:sum(-3 .. 3)"); |
| result = script.execute(null); |
| Assert.assertEquals(Integer.valueOf(0), result); |
| } |
| |
| public static class DateArithmetic extends JexlArithmetic { |
| DateArithmetic(boolean flag) { |
| super(flag); |
| } |
| |
| protected Object getDateValue(Date date, String key) { |
| try { |
| Calendar cal = Calendar.getInstance(); |
| cal.setTime(date); |
| if ("yyyy".equals(key)) { |
| return cal.get(Calendar.YEAR); |
| } else if ("MM".equals(key)) { |
| return cal.get(Calendar.MONTH) + 1; |
| } else if ("dd".equals(key)) { |
| return cal.get(Calendar.DAY_OF_MONTH); |
| } |
| // Otherwise treat as format mask |
| SimpleDateFormat df = new SimpleDateFormat(key);//, dfs); |
| return df.format(date); |
| |
| } catch (Exception ex) { |
| return null; |
| } |
| } |
| |
| protected Object setDateValue(Date date, String key, Object value) throws Exception { |
| Calendar cal = Calendar.getInstance(); |
| cal.setTime(date); |
| if ("yyyy".equals(key)) { |
| cal.set(Calendar.YEAR, toInteger(value)); |
| } else if ("MM".equals(key)) { |
| cal.set(Calendar.MONTH, toInteger(value) - 1); |
| } else if ("dd".equals(key)) { |
| cal.set(Calendar.DAY_OF_MONTH, toInteger(value)); |
| } |
| date.setTime(cal.getTimeInMillis()); |
| return date; |
| } |
| |
| public Object propertyGet(Date date, String identifier) { |
| return getDateValue(date, identifier); |
| } |
| |
| public Object propertySet(Date date, String identifier, Object value) throws Exception { |
| return setDateValue(date, identifier, value); |
| } |
| |
| public Object arrayGet(Date date, String identifier) { |
| return getDateValue(date, identifier); |
| } |
| |
| public Object arraySet(Date date, String identifier, Object value) throws Exception { |
| return setDateValue(date, identifier, value); |
| } |
| |
| public Date now() { |
| return new Date(System.currentTimeMillis()); |
| } |
| |
| public Date multiply(Date d0, Date d1) { |
| throw new ArithmeticException("unsupported"); |
| } |
| } |
| |
| public static class DateContext extends MapContext { |
| private Locale locale = Locale.US; |
| |
| void setLocale(Locale l10n) { |
| this.locale = l10n; |
| } |
| |
| public String format(Date date, String fmt) { |
| SimpleDateFormat sdf = new SimpleDateFormat(fmt, locale); |
| return sdf.format(date); |
| } |
| |
| public String format(Number number, String fmt) { |
| return new DecimalFormat(fmt).format(number); |
| } |
| } |
| |
| @Test |
| public void testOperatorError() throws Exception { |
| testOperatorError(true); |
| testOperatorError(false); |
| } |
| |
| private void testOperatorError(boolean silent) throws Exception { |
| CaptureLog log = new CaptureLog(); |
| DateContext jc = new DateContext(); |
| Date d = new Date(); |
| JexlEngine jexl = new JexlBuilder().logger(log).strict(true).silent(silent).cache(32) |
| .arithmetic(new DateArithmetic(true)).create(); |
| JexlScript expr0 = jexl.createScript("date * date", "date"); |
| try { |
| Object value0 = expr0.execute(jc, d); |
| if (!silent) { |
| Assert.fail("should have failed"); |
| } else { |
| Assert.assertEquals(1, log.count("warn")); |
| } |
| } catch(JexlException.Operator xop) { |
| Assert.assertEquals("*", xop.getSymbol()); |
| } |
| if (!silent) { |
| Assert.assertEquals(0, log.count("warn")); |
| } |
| } |
| |
| @Test |
| public void testDateArithmetic() throws Exception { |
| Date d = new Date(); |
| JexlContext jc = new MapContext(); |
| JexlEngine jexl = new JexlBuilder().cache(32).arithmetic(new DateArithmetic(true)).create(); |
| JexlScript expr0 = jexl.createScript("date.yyyy = 1969; date.MM=7; date.dd=20; ", "date"); |
| Object value0 = expr0.execute(jc, d); |
| Assert.assertNotNull(value0); |
| value0 = d; |
| //d = new Date(); |
| Assert.assertEquals(1969, jexl.createScript("date.yyyy", "date").execute(jc, value0)); |
| Assert.assertEquals(7, jexl.createScript("date.MM", "date").execute(jc, value0)); |
| Assert.assertEquals(20, jexl.createScript("date.dd", "date").execute(jc, value0)); |
| } |
| |
| @Test |
| public void testFormatArithmetic() throws Exception { |
| Calendar cal = Calendar.getInstance(); |
| cal.set(1969, 7, 20); |
| Date x0 = cal.getTime(); |
| String y0 = "MM/yy/dd"; |
| Number x1 = 42.12345; |
| String y1 = "##0.##"; |
| DateContext jc = new DateContext(); |
| JexlEngine jexl = new JexlBuilder().cache(32).arithmetic(new DateArithmetic(true)).create(); |
| JexlScript expr0 = jexl.createScript("x.format(y)", "x", "y"); |
| Object value10 = expr0.execute(jc, x0, y0); |
| Object value20 = expr0.execute(jc, x0, y0); |
| Assert.assertEquals(value10, value20); |
| Object value11 = expr0.execute(jc, x1, y1); |
| Object value21 = expr0.execute(jc, x1, y1); |
| Assert.assertEquals(value11, value21); |
| value10 = expr0.execute(jc, x0, y0); |
| Assert.assertEquals(value10, value20); |
| value11 = expr0.execute(jc, x1, y1); |
| Assert.assertEquals(value11, value21); |
| value10 = expr0.execute(jc, x0, y0); |
| Assert.assertEquals(value10, value20); |
| value11 = expr0.execute(jc, x1, y1); |
| Assert.assertEquals(value11, value21); |
| |
| JexlScript expr1 = jexl.createScript("format(x, y)", "x", "y"); |
| value10 = expr1.execute(jc, x0, y0); |
| Assert.assertEquals(value10, value20); |
| Object s0 = expr1.execute(jc, x0, "EEE dd MMM yyyy"); |
| Assert.assertEquals("Wed 20 Aug 1969", s0); |
| jc.setLocale(Locale.FRANCE); |
| s0 = expr1.execute(jc, x0, "EEE dd MMM yyyy"); |
| Assert.assertEquals("mer. 20 ao\u00fbt 1969", s0); |
| |
| expr1 = jexl.createScript("format(now(), y)", "y"); |
| Object n0 = expr1.execute(jc, y0); |
| Assert.assertNotNull(n0); |
| expr1 = jexl.createScript("now().format(y)", "y"); |
| Object n1 = expr1.execute(jc, y0); |
| Assert.assertNotNull(n0); |
| Assert.assertEquals(n0, n1); |
| } |
| |
| @Test |
| public void testFormatArithmeticJxlt() throws Exception { |
| Map<String, Object> ns = new HashMap<String, Object>(); |
| ns.put("calc", Aggregate.class); |
| Calendar cal = Calendar.getInstance(); |
| cal.set(1969, 7, 20); |
| Date x0 = cal.getTime(); |
| String y0 = "yyyy-MM-dd"; |
| DateContext jc = new DateContext(); |
| JexlEngine jexl = new JexlBuilder().cache(32).namespaces(ns).arithmetic(new DateArithmetic(true)).create(); |
| JxltEngine jxlt = jexl.createJxltEngine(); |
| |
| JxltEngine.Template expr0 = jxlt.createTemplate("${x.format(y)}", "x", "y"); |
| StringWriter strw = new StringWriter(); |
| expr0.evaluate(jc, strw, x0, y0); |
| String strws = strw.toString(); |
| Assert.assertEquals("1969-08-20", strws); |
| |
| expr0 = jxlt.createTemplate("${calc:sum(x .. y)}", "x", "y"); |
| strw = new StringWriter(); |
| expr0.evaluate(jc, strw, 1, 3); |
| strws = strw.toString(); |
| Assert.assertEquals("6", strws); |
| |
| JxltEngine.Template expr1 = jxlt.createTemplate("${jexl:include(s, x, y)}", "s", "x", "y"); |
| strw = new StringWriter(); |
| expr1.evaluate(jc, strw, expr0, 1, 3); |
| strws = strw.toString(); |
| Assert.assertEquals("6", strws); |
| |
| expr0 = jxlt.createTemplate("${now().format(y)}", "y"); |
| strw = new StringWriter(); |
| expr0.evaluate(jc, strw, y0); |
| strws = strw.toString(); |
| Assert.assertNotNull(strws); |
| } |
| } |