Skip to content
Permalink
Browse files
JEXL-342: extend arithmetic to dereference references (sic);
  • Loading branch information
henrib committed May 5, 2022
1 parent 33bc10c commit fa3e3db789e85beb7bafcfa882af43a5ae5026c8
Showing 3 changed files with 293 additions and 7 deletions.
@@ -114,7 +114,7 @@ public Object interpret(final JexlNode node) {
throw new JexlException.StackOverflow(node.jexlInfo(), "jexl (" + jexl.stackOverflow + ")", null);
}
cancelCheck(node);
return node.jjtAccept(this, null);
return arithmetic.controlReturn(node.jjtAccept(this, null));
} catch(final StackOverflowError xstack) {
final JexlException xjexl = new JexlException.StackOverflow(node.jexlInfo(), "jvm", xstack);
if (!isSilent()) {
@@ -139,13 +139,16 @@ public Object interpret(final JexlNode node) {
logger.warn(xjexl.getMessage(), xjexl.getCause());
}
} finally {
synchronized(this) {
if (functors != null) {
for (final Object functor : functors.values()) {
closeIfSupported(functor);
// clean functors at top level
if (fp == 0) {
synchronized (this) {
if (functors != null) {
for (final Object functor : functors.values()) {
closeIfSupported(functor);
}
functors.clear();
functors = null;
}
functors.clear();
functors = null;
}
}
jexl.putThreadEngine(tjexl);
@@ -0,0 +1,231 @@
/*
* 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.jexl342;

import org.apache.commons.jexl3.JexlArithmetic;

import java.lang.ref.Reference;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicReference;

/**
* Unwraps Optional/Reference/AtomicReference on public and key methods.
*/
public class OptionalArithmetic extends JexlArithmetic {
public OptionalArithmetic(boolean astrict) {
super(astrict);
}

/**
* Dereferences an Optional, a Reference or an AtomicReference, leave other as is.
* @param ref the reference
* @return the referenced object
*/
protected Object star(Object ref) {
if (ref instanceof Optional<?>) {
Optional<?> o = (Optional<?>) ref;
return o.orElse(null);
}
if (ref instanceof Reference<?>) {
Optional<?> r = (Optional<?>) ref;
return r.get();
}
if (ref instanceof AtomicReference<?>) {
AtomicReference<?> r = (AtomicReference<?>) ref;
return r.get();
}
return ref;
}

@Override
public Object controlReturn(Object returned) {
return star(returned);
}

@Override
public boolean toBoolean(final Object val) {
return super.toBoolean(star(val));
}

@Override
public String toString(final Object val) {
return super.toString(star(val));
}

@Override
public int toInteger(final Object val) {
return super.toInteger(star(val));
}

@Override
public long toLong(final Object val) {
return super.toLong(star(val));
}

@Override
public double toDouble(final Object val) {
return super.toDouble(star(val));
}

@Override
public BigInteger toBigInteger(final Object val) {
return super.toBigInteger(star(val));
}

@Override
public BigDecimal toBigDecimal(final Object val) {
return super.toBigDecimal(star(val));
}

@Override
public Integer size(final Object object, final Integer def) {
return super.size(star(object), def);
}

@Override
public Boolean empty(Object o) {
return super.empty(star(o));
}

@Override
public Boolean isEmpty(final Object object, final Boolean def) {
return super.isEmpty(star(object), def);
}

@Override
public Object positivize(Object o) {
return super.positivize(star(o));
}

@Override
public Object negate(Object o) {
return super.negate(star(o));
}

@Override
public Object complement(Object o) {
return super.complement(star(o));
}

@Override
public Boolean contains(Object lhs, Object rhs) {
return super.contains(star(lhs), star(rhs));
}

@Override
public Object add(Object lhs, Object rhs) {
return super.add(star(lhs), star(rhs));
}

@Override
public Object subtract(Object lhs, Object rhs) {
return super.subtract(star(lhs), star(rhs));
}

@Override
public Object multiply(Object lhs, Object rhs) {
return super.multiply(star(lhs), star(rhs));
}

@Override
public Object divide(Object lhs, Object rhs) {
return super.divide(star(lhs), star(rhs));
}

@Override
public Object mod(Object lhs, Object rhs) {
return super.mod(star(lhs), star(rhs));
}

@Override
public Object and(final Object left, final Object right) { return super.and(star(left), star(right)); }

@Override
public Object or(final Object left, final Object right) { return super.or(star(left), star(right)); }

@Override
public Object xor(final Object left, final Object right) { return super.xor(star(left), star(right)); }

@Override
public Object shiftLeft(final Object left, final Object right) { return super.shiftLeft(star(left), star(right)); }

@Override
public Object shiftRight(final Object left, final Object right) { return super.shiftRight(star(left), star(right)); }

@Override
public Object shiftRightUnsigned(final Object left, final Object right) { return super.shiftRightUnsigned(star(left), star(right)); }

@Override
public Boolean startsWith(final Object left, final Object right) {
return super.startsWith(star(left), star(right));
}

@Override
public Boolean endsWith(final Object left, final Object right) {
return super.endsWith(star(left), star(right));
}

@Override
public boolean equals(final Object left, final Object right) {
return equals(star(left), star(right));
}

@Override
public boolean greaterThan(final Object left, final Object right) {
return greaterThan(star(left), star(right));
}

@Override
public boolean greaterThanOrEqual(final Object left, final Object right) {
return greaterThanOrEqual(star(left), star(right));
}

@Override
public boolean lessThan(final Object left, final Object right) {
return lessThan(star(left), star(right));
}

@Override
public boolean lessThanOrEqual(final Object left, final Object right) {
return lessThanOrEqual(star(left), star(right));
}

@Override
public boolean narrowArguments(final Object[] args) {
boolean narrowed = false;
if (args != null) {
for (int a = 0; a < args.length; ++a) {
final Object arg = args[a];
Object sarg = star(arg);
if (sarg != arg) {
narrowed = true;
}
if (arg instanceof Number) {
final Number narg = (Number) arg;
final Number narrow = narrow(narg);
if (!narg.equals(narrow)) {
args[a] = narrow;
narrowed = true;
}
}
}
}
return narrowed;
}
}
@@ -16,18 +16,26 @@
*/
package org.apache.commons.jexl3.jexl342;

import org.apache.commons.jexl3.JexlArithmetic;
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlException;
import org.apache.commons.jexl3.JexlFeatures;
import org.apache.commons.jexl3.JexlInfo;
import org.apache.commons.jexl3.JexlScript;
import org.apache.commons.jexl3.MapContext;
import org.apache.commons.jexl3.introspection.JexlUberspect;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;

public class OptionalTest {

@@ -45,6 +53,50 @@ public Optional<List<String>> findNames() {
}
}

public static class StreamContext extends MapContext {
public Stream map(Collection<Object> c, JexlScript s) {
JexlContext context = JexlEngine.getThreadContext();
return c.stream().map(a->s.execute(context, a));
}
public Object reduce(Stream<Object> stream, JexlScript script) {
return stream.reduce((identity, element)->{
JexlContext context = JexlEngine.getThreadContext();
return script.execute(context, identity, element);
});
}
}

@Test
public void testStream() {
String src = "[1, 2, 3, ...].map(x -> x * x).reduce((acc, x)->acc + x)";
JexlBuilder builder = new JexlBuilder();
JexlUberspect uber = builder.create().getUberspect();
JexlArithmetic jexla = new OptionalArithmetic(true);
JexlEngine jexl = builder.uberspect(new ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create();
JexlInfo info = new JexlInfo("testStream", 1, 1);
MapContext context = new StreamContext();
JexlScript script = jexl.createScript(src, "list");
Object result = script.execute(context, Arrays.asList(1, 2, 3));
Assert.assertEquals(14, result);
//Optional<?> result = (Optional<?>) script.execute(context, Arrays.asList(1, 2, 3));
//Assert.assertEquals(14, result.get());
}

@Test
public void testOptionalArgs() {
JexlBuilder builder = new JexlBuilder();
JexlArithmetic jexla = new OptionalArithmetic(true);
JexlUberspect uber = builder.create().getUberspect();
JexlEngine jexl = builder.uberspect(new ReferenceUberspect(uber)).arithmetic(jexla).safe(false).create();
JexlInfo info = new JexlInfo("testStream", 1, 1);
MapContext context = new StreamContext();
String src = "x + x";
JexlScript script = jexl.createScript(src, "x");
Optional<Integer> x = Optional.of(21);
Object result = script.execute(context, x);
Assert.assertEquals(42, result);
}

@Test
public void test342() {
JexlBuilder builder = new JexlBuilder();

0 comments on commit fa3e3db

Please sign in to comment.