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>


Reply via email to