Author: henrib Date: Thu Jul 30 16:57:48 2015 New Revision: 1693456 URL: http://svn.apache.org/r1693456 Log: JEXL: JEXL-171: made property executors discovery customizable, use different strategies when solving '.' (POJO) and '[]' (Map); added specific tests; nitpicks here and there
Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/IntegerRange.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/LongRange.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java commons/proper/jexl/trunk/src/site/xdoc/changes.xml Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlOperator.java Thu Jul 30 16:57:48 2015 @@ -36,168 +36,168 @@ package org.apache.commons.jexl3; public enum JexlOperator { /** * <strong>Syntax:</strong> <code>x + y</code> - * <br><strong>Method:</strong> <code>T add(L x, R y);</code> + * <br><strong>Method:</strong> <code>T add(L x, R y);</code>. * @see JexlArithmetic#add */ ADD("+", "add", 2), /** * <strong>Syntax:</strong> <code>x - y</code> - * <br><strong>Method:</strong> <code>T subtract(L x, R y);</code> + * <br><strong>Method:</strong> <code>T subtract(L x, R y);</code>. * @see JexlArithmetic#subtract */ SUBTRACT("-", "subtract", 2), /** * <strong>Syntax:</strong> <code>x * y</code> - * <br><strong>Method:</strong> <code>T multiply(L x, R y);</code> + * <br><strong>Method:</strong> <code>T multiply(L x, R y);</code>. * @see JexlArithmetic#multiply */ MULTIPLY("*", "multiply", 2), /** * <strong>Syntax:</strong> <code>x / y</code> - * <br><strong>Method:</strong> <code>T divide(L x, R y);</code> + * <br><strong>Method:</strong> <code>T divide(L x, R y);</code>. * @see JexlArithmetic#divide */ DIVIDE("/", "divide", 2), /** * <strong>Syntax:</strong> <code>x % y</code> - * <br><strong>Method:</strong> <code>T mod(L x, R y);</code> + * <br><strong>Method:</strong> <code>T mod(L x, R y);</code>. * @see JexlArithmetic#mod */ MOD("%", "mod", 2), /** * <strong>Syntax:</strong> <code>x & y</code> - * <br><strong>Method:</strong> <code>T and(L x, R y);</code> + * <br><strong>Method:</strong> <code>T and(L x, R y);</code>. * @see JexlArithmetic#and */ AND("&", "and", 2), /** * <strong>Syntax:</strong> <code>x | y</code> - * <br><strong>Method:</strong> <code>T or(L x, R y);</code> + * <br><strong>Method:</strong> <code>T or(L x, R y);</code>. * @see JexlArithmetic#or */ OR("|", "or", 2), /** * <strong>Syntax:</strong> <code>x ^ y</code> - * <br><strong>Method:</strong> <code>T xor(L x, R y);</code> + * <br><strong>Method:</strong> <code>T xor(L x, R y);</code>. * @see JexlArithmetic#xor */ XOR("^", "xor", 2), /** * <strong>Syntax:</strong> <code>x == y</code> - * <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean equals(L x, R y);</code>. * @see JexlArithmetic#equals */ EQ("==", "equals", 2), /** * <strong>Syntax:</strong> <code>x < y</code> - * <br><strong>Method:</strong> <code>boolean lessThan(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean lessThan(L x, R y);</code>. * @see JexlArithmetic#lessThan */ LT("<", "lessThan", 2), /** * <strong>Syntax:</strong> <code>x <= y</code> - * <br><strong>Method:</strong> <code>boolean lessThanOrEqual(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean lessThanOrEqual(L x, R y);</code>. * @see JexlArithmetic#lessThanOrEqual */ LTE("<=", "lessThanOrEqual", 2), /** * <strong>Syntax:</strong> <code>x > y</code> - * <br><strong>Method:</strong> <code>boolean greaterThan(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean greaterThan(L x, R y);</code>. * @see JexlArithmetic#greaterThan */ GT(">", "greaterThan", 2), /** * <strong>Syntax:</strong> <code>x >= y</code> - * <br><strong>Method:</strong> <code>boolean greaterThanOrEqual(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean greaterThanOrEqual(L x, R y);</code>. * @see JexlArithmetic#greaterThanOrEqual */ GTE(">=", "greaterThanOrEqual", 2), /** * <strong>Syntax:</strong> <code>x =~ y</code> - * <br><strong>Method:</strong> <code>boolean contains(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean contains(L x, R y);</code>. * @see JexlArithmetic#contains */ CONTAINS("=~", "contains", 2), /** * <strong>Syntax:</strong> <code>x =^ y</code> - * <br><strong>Method:</strong> <code>boolean startsWith(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean startsWith(L x, R y);</code>. * @see JexlArithmetic#startsWith */ STARTSWITH("=^", "startsWith", 2), /** * <strong>Syntax:</strong> <code>x =$ y</code> - * <br><strong>Method:</strong> <code>boolean endsWith(L x, R y);</code> + * <br><strong>Method:</strong> <code>boolean endsWith(L x, R y);</code>. * @see JexlArithmetic#endsWith */ ENDSWITH("=$", "endsWith", 2), /** * <strong>Syntax:</strong> <code>!x</code> - * <br><strong>Method:</strong> <code>T not(L x);</code> + * <br><strong>Method:</strong> <code>T not(L x);</code>. * @see JexlArithmetic#not */ NOT("!", "not", 1), /** * <strong>Syntax:</strong> <code>~x</code> - * <br><strong>Method:</strong> <code>T complement(L x);</code> + * <br><strong>Method:</strong> <code>T complement(L x);</code>. * @see JexlArithmetic#complement */ COMPLEMENT("~", "complement", 1), /** * <strong>Syntax:</strong> <code>-x</code> - * <br><strong>Method:</strong> <code>T negate(L x);</code> + * <br><strong>Method:</strong> <code>T negate(L x);</code>. * @see JexlArithmetic#negate */ NEGATE("-", "negate", 1), /** * <strong>Syntax:</strong> <code>empty x</code> or <code>empty(x)</code> - * <br><strong>Method:</strong> <code>boolean isEmpty(L x);</code> + * <br><strong>Method:</strong> <code>boolean isEmpty(L x);</code>. * @see JexlArithmetic#isEmpty */ EMPTY("empty", "empty", 1), /** * <strong>Syntax:</strong> <code>size x</code> or <code>size(x)</code> - * <br><strong>Method:</strong> <code>int size(L x);</code> + * <br><strong>Method:</strong> <code>int size(L x);</code>. * @see JexlArithmetic#size */ SIZE("size", "size", 1), /** * <strong>Syntax:</strong> <code>x += y</code> - * <br><strong>Method:</strong> <code>T selfAdd(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfAdd(L x, R y);</code>. */ SELF_ADD("+=", "selfAdd", ADD), /** * <strong>Syntax:</strong> <code>x -= y</code> - * <br><strong>Method:</strong> <code>T selfSubtract(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfSubtract(L x, R y);</code>. */ SELF_SUBTRACT("-=", "selfSubtract", SUBTRACT), /** * <strong>Syntax:</strong> <code>x *= y</code> - * <br><strong>Method:</strong> <code>T selfMultiply(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfMultiply(L x, R y);</code>. */ SELF_MULTIPLY("*=", "selfMultiply", MULTIPLY), /** * <strong>Syntax:</strong> <code>x /= y</code> - * <br><strong>Method:</strong> <code>T selfDivide(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfDivide(L x, R y);</code>. */ SELF_DIVIDE("/=", "selfDivide", DIVIDE), /** * <strong>Syntax:</strong> <code>x %= y</code> - * <br><strong>Method:</strong> <code>T selfMod(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfMod(L x, R y);</code>. */ SELF_MOD("%=", "selfMod", MOD), /** * <strong>Syntax:</strong> <code>x &= y</code> - * <br><strong>Method:</strong> <code>T selfAnd(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfAnd(L x, R y);</code>. */ SELF_AND("&=", "selfAnd", AND), /** * <strong>Syntax:</strong> <code>x |= y</code> - * <br><strong>Method:</strong> <code>T selfOr(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfOr(L x, R y);</code>. */ SELF_OR("|=", "selfOr", OR), /** * <strong>Syntax:</strong> <code>x ^= y</code> - * <br><strong>Method:</strong> <code>T selfXor(L x, R y);</code> + * <br><strong>Method:</strong> <code>T selfXor(L x, R y);</code>. */ SELF_XOR("^=", "selfXor", XOR), /** Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/IntegerRange.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/IntegerRange.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/IntegerRange.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/IntegerRange.java Thu Jul 30 16:57:48 2015 @@ -18,6 +18,7 @@ package org.apache.commons.jexl3.interna import java.lang.reflect.Array; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.NoSuchElementException; @@ -73,8 +74,10 @@ public abstract class IntegerRange imple @Override public int hashCode() { int hash = getClass().hashCode(); + //CHECKSTYLE:OFF Not magic number hash = 13 * hash + this.min; hash = 13 * hash + this.max; + //CHECKSTYLE:ON return hash; } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Thu Jul 30 16:57:48 2015 @@ -100,6 +100,7 @@ import org.apache.commons.jexl3.parser.N import org.apache.commons.jexl3.parser.ParserVisitor; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import java.util.Map; import org.apache.log4j.Logger; @@ -1628,7 +1629,9 @@ public class Interpreter extends ParserV } // resolve that property Exception xcause = null; - JexlPropertyGet vg = uberspect.getPropertyGet(object, attribute); + List<JexlUberspect.ResolverType> strategy = (node == null) || !(node.jjtGetParent()instanceof ASTArrayAccess) + ? JexlUberspect.POJO : JexlUberspect.MAP; + JexlPropertyGet vg = uberspect.getPropertyGet(strategy, object, attribute); if (vg != null) { try { Object value = vg.invoke(object); @@ -1689,7 +1692,9 @@ public class Interpreter extends ParserV } } Exception xcause = null; - JexlPropertySet vs = uberspect.getPropertySet(object, attribute, value); + List<JexlUberspect.ResolverType> strategy = (node == null) || !(node.jjtGetParent()instanceof ASTArrayAccess) + ? JexlUberspect.POJO : JexlUberspect.MAP; + JexlPropertySet vs = uberspect.getPropertySet(strategy, object, attribute, value); // if we can't find an exact match, narrow the value argument and try again if (vs == null) { // replace all numbers with the smallest type that will fit Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/LongRange.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/LongRange.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/LongRange.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/LongRange.java Thu Jul 30 16:57:48 2015 @@ -75,8 +75,10 @@ public abstract class LongRange implemen @Override public int hashCode() { int hash = getClass().hashCode(); - hash = 41 * hash + (int) (this.min ^ (this.min >>> 32)); - hash = 41 * hash + (int) (this.max ^ (this.max >>> 32)); + //CHECKSTYLE:OFF Not magic number + hash = 13 * hash + (int) (this.min ^ (this.min >>> 32)); + hash = 13 * hash + (int) (this.max ^ (this.max >>> 32)); + //CHECKSTYLE:ON return hash; } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/DuckSetExecutor.java Thu Jul 30 16:57:48 2015 @@ -22,10 +22,16 @@ import java.lang.reflect.InvocationTarge * Specialized executor to set a property of an object. * <p>Duck as in duck-typing for an interface like: * <code> - * interface Set { + * interface Setable { * Object set(Object property, Object value); * } * </code> + * or + * <code> + * interface Putable { + * Object put(Object property, Object value); + * } + * </code> * </p> * @since 2.0 */ @@ -44,6 +50,9 @@ public final class DuckSetExecutor exten */ public static DuckSetExecutor discover(Introspector is, Class<?> clazz, Object key, Object value) { java.lang.reflect.Method method = is.getMethod(clazz, "set", makeArgs(key, value)); + if (method == null) { + method = is.getMethod(clazz, "put", makeArgs(key, value)); + } return method == null? null : new DuckSetExecutor(clazz, method, key); } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/SandboxUberspect.java Thu Jul 30 16:57:48 2015 @@ -24,9 +24,10 @@ import org.apache.commons.jexl3.introspe import org.apache.commons.jexl3.introspection.JexlUberspect; import java.util.Iterator; +import java.util.List; /** - * An uberspect that controls usage of properties, methods and contructors through a sandbox. + * An uberspect that controls usage of properties, methods and constructors through a sandbox. * @since 3.0 */ public final class SandboxUberspect implements JexlUberspect { @@ -107,10 +108,18 @@ public final class SandboxUberspect impl */ @Override public JexlPropertyGet getPropertyGet(Object obj, Object identifier) { + return getPropertyGet(POJO, obj, identifier); + } + + /** + * {@inheritDoc} + */ + @Override + public JexlPropertyGet getPropertyGet(List<ResolverType> strategy, Object obj, Object identifier) { if (obj != null && identifier != null) { String actual = sandbox.read(obj.getClass().getName(), identifier.toString()); if (actual != null) { - return uberspect.getPropertyGet(obj, actual); + return uberspect.getPropertyGet(strategy, obj, actual); } } return null; @@ -121,10 +130,18 @@ public final class SandboxUberspect impl */ @Override public JexlPropertySet getPropertySet(final Object obj, final Object identifier, Object arg) { + return getPropertySet(POJO, obj, identifier, arg); + } + + /** + * {@inheritDoc} + */ + @Override + public JexlPropertySet getPropertySet(final List<ResolverType> strategy, final Object obj, final Object identifier, Object arg) { if (obj != null && identifier != null) { String actual = sandbox.write(obj.getClass().getName(), identifier.toString()); if (actual != null) { - return uberspect.getPropertySet(obj, actual, arg); + return uberspect.getPropertySet(strategy, obj, actual, arg); } } return null; Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/introspection/Uberspect.java Thu Jul 30 16:57:48 2015 @@ -16,7 +16,6 @@ */ package org.apache.commons.jexl3.internal.introspection; - import org.apache.commons.jexl3.JexlArithmetic; import org.apache.commons.jexl3.JexlEngine; import org.apache.commons.jexl3.JexlOperator; @@ -40,11 +39,13 @@ import java.util.concurrent.ConcurrentHa import java.lang.ref.Reference; import java.lang.ref.SoftReference; +import java.util.List; /** * Implementation of Uberspect to provide the default introspective * functionality of JEXL. - * <p>This is the class to derive to customize introspection.</p> + * <p> + * This is the class to derive to customize introspection.</p> * * @since 1.0 */ @@ -63,7 +64,8 @@ public class Uberspect implements JexlUb private volatile Reference<ClassLoader> loader; /** * The map from arithmetic classes to overloaded operator sets. - * <p>This keeps track of which operator methods are overloaded per JexlArithemtic classes + * <p> + * This keeps track of which operator methods are overloaded per JexlArithemtic classes * allowing a fail fast test during interpretation by avoiding seeking a method when there is none. */ private final Map<Class<? extends JexlArithmetic>, Set<JexlOperator>> operatorMap; @@ -82,7 +84,8 @@ public class Uberspect implements JexlUb /** * Gets the current introspector base. - * <p>If the reference has been collected, this method will recreate the underlying introspector.</p> + * <p> + * If the reference has been collected, this method will recreate the underlying introspector.</p> * @return the introspector */ // CSOFF: DoubleCheckedLocking @@ -215,112 +218,109 @@ public class Uberspect implements JexlUb @Override public JexlPropertyGet getPropertyGet(Object obj, Object identifier) { + return getPropertyGet(POJO, obj, identifier); + } + + @Override + public JexlPropertyGet getPropertyGet(final List<ResolverType> strategy, final Object obj, final Object identifier) { final Class<?> claz = obj.getClass(); final String property = AbstractExecutor.castString(identifier); final Introspector is = base(); - JexlPropertyGet executor; - // first try for a getFoo() type of property (also getfoo() ) - if (property != null) { - executor = PropertyGetExecutor.discover(is, claz, property); - if (executor != null) { - return executor; - } - // look for boolean isFoo() - executor = BooleanGetExecutor.discover(is, claz, property); - if (executor != null) { - return executor; + JexlPropertyGet executor = null; + for (ResolverType resolver : strategy) { + switch (resolver) { + case PROPERTY: + // first try for a getFoo() type of property (also getfoo() ) + executor = PropertyGetExecutor.discover(is, claz, property); + if (executor == null) { + executor = BooleanGetExecutor.discover(is, claz, property); + } + break; + case MAP: + // let's see if we are a map... + executor = MapGetExecutor.discover(is, claz, identifier); + break; + case LIST: + // let's see if this is a list or array + Integer index = AbstractExecutor.castInteger(identifier); + if (index != null) { + executor = ListGetExecutor.discover(is, claz, index); + } + break; + case DUCK: + // if that didn't work, look for get(foo) + executor = DuckGetExecutor.discover(is, claz, identifier); + if (executor == null && property != null && property != identifier) { + // look for get("foo") if we did not try yet (just above) + executor = DuckGetExecutor.discover(is, claz, property); + } + break; + case FIELD: + // a field may be? (can not be a number) + executor = FieldGetExecutor.discover(is, claz, property); + break; + case CONTAINER: + // or an indexed property? + executor = IndexedType.discover(is, obj, property); + break; + default: + continue; // in case we add new ones in enum } - } - // let's see if we are a map... - executor = MapGetExecutor.discover(is, claz, identifier); - if (executor != null) { - return executor; - } - // let's see if this is a list or array - Integer index = AbstractExecutor.castInteger(identifier); - if (index != null) { - executor = ListGetExecutor.discover(is, claz, index); if (executor != null) { return executor; } } - // if that didn't work, look for get(foo) - executor = DuckGetExecutor.discover(is, claz, identifier); - if (executor != null) { - return executor; - } - if (property != null) { - // look for get("foo") if we did not try yet (just above) - if (property != identifier) { - executor = DuckGetExecutor.discover(is, claz, property); - if (executor != null) { - return executor; - } - } - if (index == null) { - // a field may be? (can not be a number) - executor = FieldGetExecutor.discover(is, claz, property); - if (executor != null) { - return executor; - } - // or an indexed property? - executor = IndexedType.discover(is, obj, property); - if (executor != null) { - return executor; - } - } - } return null; } @Override - public JexlPropertySet getPropertySet(final Object obj, final Object identifier, Object arg) { + public JexlPropertySet getPropertySet(final Object obj, final Object identifier, final Object arg) { + return getPropertySet(POJO, obj, identifier, arg); + } + + @Override + public JexlPropertySet getPropertySet(final List<ResolverType> strategy, final Object obj, final Object identifier, final Object arg) { final Class<?> claz = obj.getClass(); final String property = AbstractExecutor.castString(identifier); final Introspector is = base(); - JexlPropertySet executor; - // first try for a setFoo() type of property (also setfoo() ) - if (property != null) { - executor = PropertySetExecutor.discover(is, claz, property, arg); - if (executor != null) { - return executor; + JexlPropertySet executor = null; + for (ResolverType resolver : strategy) { + switch (resolver) { + case PROPERTY: + // first try for a setFoo() type of property (also setfoo() ) + executor = PropertySetExecutor.discover(is, claz, property, arg); + break; + case MAP: + // let's see if we are a map... + executor = MapSetExecutor.discover(is, claz, identifier, arg); + break; + case LIST: + // let's see if we can convert the identifier to an int, + // if obj is an array or a list, we can still do something + Integer index = AbstractExecutor.castInteger(identifier); + if (index != null) { + executor = ListSetExecutor.discover(is, claz, identifier, arg); + } + break; + case DUCK: + // if that didn't work, look for set(foo) + executor = DuckSetExecutor.discover(is, claz, identifier, arg); + if (executor == null && property != null && property != identifier) { + executor = DuckSetExecutor.discover(is, claz, property, arg); + } + break; + case FIELD: + // a field may be? + executor = FieldSetExecutor.discover(is, claz, property, arg); + break; + case CONTAINER: + default: + continue; // in case we add new ones in enum } - } - // let's see if we are a map... - executor = MapSetExecutor.discover(is, claz, identifier, arg); - if (executor != null) { - return executor; - } - // let's see if we can convert the identifier to an int, - // if obj is an array or a list, we can still do something - Integer index = AbstractExecutor.castInteger(identifier); - if (index != null) { - executor = ListSetExecutor.discover(is, claz, identifier, arg); if (executor != null) { return executor; } } - // if that didn't work, look for set(foo) - executor = DuckSetExecutor.discover(is, claz, identifier, arg); - if (executor != null) { - return executor; - } - // last, look for set("foo") if we did not try yet - if (property != null) { - if (property != identifier) { - executor = DuckSetExecutor.discover(is, claz, property, arg); - if (executor != null) { - return executor; - } - } - if (index == null) { - // a field may be? - executor = FieldSetExecutor.discover(is, claz, property, arg); - if (executor != null) { - return executor; - } - } - } return null; } @@ -439,64 +439,4 @@ public class Uberspect implements JexlUb } return jau; } - - /** - * May be a way to extend/improve sandboxing by choosing actual method for resolution. - ** - * public static enum GetResolver { - * PROPERTY { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return PropertyGetExecutor.discover(uberspect.base(), obj.getClass(), AbstractExecutor.toString(identifier)); - * } - * }, - * BOOLEAN { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return BooleanGetExecutor.discover(uberspect.base(), obj.getClass(), AbstractExecutor.toString(identifier)); - * } - * }, - * MAP { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return MapGetExecutor.discover(uberspect.base(), obj.getClass(), identifier); - * } - * }, - * LIST { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return ListGetExecutor.discover(uberspect.base(), obj.getClass(), identifier); - * } - * }, - * DUCK { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * final Introspector is = uberspect.base(); - * final Class<?> clazz = obj.getClass(); - * JexlPropertyGet executor = DuckGetExecutor.discover(is, clazz, identifier); - * if (executor == null && identifier != null && !(identifier instanceof String)) { - * executor = DuckGetExecutor.discover(is, clazz, AbstractExecutor.toString(identifier)); - * } - * return executor; - * } - * }, - * FIELD { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return FieldGetExecutor.discover(uberspect.base(), obj.getClass(), AbstractExecutor.toString(identifier)); - * } - * }, - * INDEXED { - * @Override - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return IndexedType.discover(uberspect.base(), obj, AbstractExecutor.toString(identifier)); - * } - * }, - * ANY {}; - * <p/> - * public JexlPropertyGet resolve(Uberspect uberspect, Object obj, Object identifier) { - * return uberspect.getPropertyGet(obj, identifier); - * } - * } - */ } Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java (original) +++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/introspection/JexlUberspect.java Thu Jul 30 16:57:48 2015 @@ -18,7 +18,10 @@ package org.apache.commons.jexl3.introspection; import org.apache.commons.jexl3.JexlArithmetic; +import java.util.Arrays; +import java.util.Collections; import java.util.Iterator; +import java.util.List; /** * 'Federated' introspection/reflection interface to allow JEXL introspection @@ -28,6 +31,68 @@ import java.util.Iterator; */ public interface JexlUberspect { /** + * The various property resolver types. + * <p>These are used to compose 'strategies' to solve properties; a strategy is an array (list) of resolver types. + * Each resolver type discovers how to set/get a property with different techniques; seeking + * method names or field names, etc. + * In a strategy, these are tried in sequence and the first non-null resolver stops the search. + */ + enum ResolverType { + /** + * Seeks methods named get{P,p}property and is{P,p}property. + */ + PROPERTY, + /** + * Seeks map methods get/put. + */ + MAP, + /** + * Seeks list methods get/set. + */ + LIST, + /** + * Seeks any get/{set,put} method (quacking like a list or a map). + */ + DUCK, + /** + * Seeks public instance members. + */ + FIELD, + /** + * Seeks a getContainer(property) and setContainer(property, value) + * as in <code>x.container.property</code>. + */ + CONTAINER + } + + /** + * A resolver strategy tailored for POJOs, favors '.' over '[]'. + * This is the default strategy for getPropertyGet/getPropertySet. + * @see JexlUberspect#getPropertyGet + * @see JexlUberspect#getPropertySet + */ + static final List<ResolverType> POJO = Collections.unmodifiableList(Arrays.asList( + ResolverType.PROPERTY, + ResolverType.MAP, + ResolverType.LIST, + ResolverType.DUCK, + ResolverType.FIELD, + ResolverType.CONTAINER + )); + + /** + * A resolver strategy tailored for Maps, favors '[]' over '.'. + */ + static final List<ResolverType> MAP = Collections.unmodifiableList(Arrays.asList( + ResolverType.MAP, + ResolverType.LIST, + ResolverType.DUCK, + ResolverType.PROPERTY, + ResolverType.FIELD, + ResolverType.CONTAINER + )); + + /** * Sets the class loader to use. * <p>This increments the version.</p> * @param loader the class loader @@ -60,34 +125,57 @@ public interface JexlUberspect { /** * Property getter. - * <p>Returns JexlPropertyGet appropos for ${bar.woogie}. + * <p>returns a JelPropertySet apropos to an expression like <code>bar.woogie</code>.</p> * @param obj the object to get the property from * @param identifier property name - * @return a {@link JexlPropertyGet} + * @return a {@link JexlPropertyGet} or null */ JexlPropertyGet getPropertyGet(Object obj, Object identifier); /** + * Property getter. + * <p>Seeks a JexlPropertyGet apropos to an expression like <code>bar.woogie</code>.</p> + * @param strategy the ordered list of resolver types + * @param obj the object to get the property from + * @param identifier property name + * @return a {@link JexlPropertyGet} or null + * @since 3.0 + */ + JexlPropertyGet getPropertyGet(List<ResolverType> strategy, Object obj, Object identifier); + + /** * Property setter. - * <p>returns JelPropertySet appropos for ${foo.bar = "geir"}</p>. + * <p>Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> * @param obj the object to get the property from. * @param identifier property name * @param arg value to set - * @return a {@link JexlPropertySet}. + * @return a {@link JexlPropertySet} or null */ JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg); /** + * Property setter. + * <p>Seeks a JelPropertySet apropos to an expression like <code>foo.bar = "geir"</code>.</p> + * @param strategy the ordered list of resolver types + * @param obj the object to get the property from + * @param identifier property name + * @param arg value to set + * @return a {@link JexlPropertySet} or null + * @since 3.0 + */ + JexlPropertySet getPropertySet(List<ResolverType> strategy, Object obj, Object identifier, Object arg); + + /** * Gets an iterator from an object. * @param obj to get the iterator from - * @return an iterator over obj + * @return an iterator over obj or null */ Iterator<?> getIterator(Object obj); /** * Gets an arithmetic operator resolver for a given arithmetic instance. * @param arithmetic the arithmetic instance - * @return the arithmetic uberspect or null if no operator method were override + * @return the arithmetic uberspect or null if no operator method were overridden * @since 3.0 */ JexlArithmetic.Uberspect getArithmetic(JexlArithmetic arithmetic); Modified: commons/proper/jexl/trunk/src/site/xdoc/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/site/xdoc/changes.xml?rev=1693456&r1=1693455&r2=1693456&view=diff ============================================================================== --- commons/proper/jexl/trunk/src/site/xdoc/changes.xml (original) +++ commons/proper/jexl/trunk/src/site/xdoc/changes.xml Thu Jul 30 16:57:48 2015 @@ -26,6 +26,9 @@ </properties> <body> <release version="3.0" date="unreleased"> + <action dev="henrib" type="fix" issue="JEXL-171" due-to="Dmitri Blinov"> + Map access operator does not work if key name clashes with map property name + </action> <action dev="henrib" type="add" issue="JEXL-170"> Implement assignment operators </action>