Author: henrib
Date: Thu Jan  7 18:21:29 2010
New Revision: 896952

URL: http://svn.apache.org/viewvc?rev=896952&view=rev
Log:
Made public fields 'first class' properties;
Added a field cache to ClassMap;
Added getField/getFieldNames in appropriate classes;
Updated tests accordingly

Modified:
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
    
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
    
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/IssuesTest.java
    
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/JexlEngine.java
 Thu Jan  7 18:21:29 2010
@@ -116,7 +116,7 @@
      */
     private static final class UberspectHolder {
         /** The default uberspector that handles all introspection patterns. */
-        private static final Uberspect UBERSPECT = new 
UberspectImpl(LogFactory.getLog(JexlEngine.class), false);
+        private static final Uberspect UBERSPECT = new 
UberspectImpl(LogFactory.getLog(JexlEngine.class));
         /** Non-instantiable. */
         private UberspectHolder() {}
     }
@@ -202,7 +202,7 @@
         if (logger == null || 
logger.equals(LogFactory.getLog(JexlEngine.class))) {
             return UberspectHolder.UBERSPECT;
         }
-        return new UberspectImpl(logger, false);
+        return new UberspectImpl(logger);
     }
 
     /**

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/Introspector.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/Introspector.java
 Thu Jan  7 18:21:29 2010
@@ -19,6 +19,7 @@
 import java.lang.ref.SoftReference;
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 
 import org.apache.commons.jexl2.internal.introspection.IntrospectorBase;
 import org.apache.commons.jexl2.internal.introspection.MethodKey;
@@ -105,6 +106,26 @@
 
 
     /**
+     * Gets the field named by <code>key</code> for the class <code>c</code>.
+     *
+     * @param c     Class in which the field search is taking place
+     * @param key   Name of the field being searched for
+     * @return the desired field or null if it does not exist or is not 
accessible
+     * */
+    protected final Field getField(Class<?> c, String key) {
+        return base().getField(c, key);
+    }
+
+    /**
+     * Gets the accessible field names known for a given class.
+     * @param c the class
+     * @return the class field names
+     */
+    public final String[] getFieldNames(Class<?> c) {
+        return base().getFieldNames(c);
+    }
+
+    /**
      * Gets the method defined by <code>name</code> and
      * <code>params</code> for the Class <code>c</code>.
      *

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/ClassMap.java
 Thu Jan  7 18:21:29 2010
@@ -16,8 +16,10 @@
  */
 package org.apache.commons.jexl2.internal.introspection;
 
+import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -43,6 +45,8 @@
 final class ClassMap {
     /** cache of methods. */
     private final MethodCache methodCache;
+    /** cache of fields. */
+    private final Map<String, Field> fieldCache;
 
     /**
      * Standard constructor.
@@ -50,11 +54,52 @@
      * @param aClass the class to deconstruct.
      * @param log the logger.
      */
-    public ClassMap(Class<?> aClass, Log log) {
+    ClassMap(Class<?> aClass, Log log) {
+        // eagerly cache methods
         methodCache = createMethodCache(aClass, log);
+        // eagerly cache public fields
+        fieldCache = createFieldCache(aClass);
     }
 
     /**
+     * Find a Field using its name.
+     * <p>The clazz parameter <strong>must</strong> be this ClassMap key.</p>
+     * @param clazz the class to introspect
+     * @param fname the field name
+     * @return A Field object representing the field to invoke or null.
+     */
+    Field findField(final Class<?> clazz, final String fname) {
+        return fieldCache.get(fname);
+    }
+
+    /**
+     * Gets the field names cached by this map.
+     * @return the array of field names
+     */
+    String[] getFieldNames() {
+        return fieldCache.keySet().toArray(new String[fieldCache.size()]);
+    }
+
+    /**
+     * Creates a map of all public fields of a given class.
+     * @param clazz the class to introspect
+     * @return the map of fields (may be the empty map, can not be null)
+     */
+    private static Map<String,Field> createFieldCache(Class<?> clazz) {
+        Field[] fields = clazz.getFields();
+        if (fields.length > 0) {
+            Map<String, Field> cache = new HashMap<String, Field>();
+            for(Field field : fields) {
+                cache.put(field.getName(), field);
+            }
+            return cache;
+        } else {
+            return Collections.emptyMap();
+        }
+    }
+
+
+    /**
      * Gets the methods names cached by this map.
      * @return the array of method names
      */
@@ -69,7 +114,7 @@
      * @return A Method object representing the method to invoke or null.
      * @throws MethodKey.AmbiguousException When more than one method is a 
match for the parameters.
      */
-    public Method findMethod(final MethodKey key)
+    Method findMethod(final MethodKey key)
             throws MethodKey.AmbiguousException {
         return methodCache.get(key);
     }
@@ -220,7 +265,7 @@
          * name and actual arguments used to find it.
          * </p>
          */
-        private final Map<MethodKey, Method> cache = new HashMap<MethodKey, 
Method>();
+        private final Map<MethodKey, Method> methods = new HashMap<MethodKey, 
Method>();
         /**
          * Map of methods that are searchable according to method parameters 
to find a match.
          */
@@ -242,10 +287,9 @@
          * @return A Method object representing the method to invoke or null.
          * @throws MethodKey.AmbiguousException When more than one method is a 
match for the parameters.
          */
-        Method get(final MethodKey methodKey)
-                throws MethodKey.AmbiguousException {
+        Method get(final MethodKey methodKey) throws 
MethodKey.AmbiguousException {
             synchronized (methodMap) {
-                Method cacheEntry = cache.get(methodKey);
+                Method cacheEntry = methods.get(methodKey);
                 // We looked this up before and failed.
                 if (cacheEntry == CACHE_MISS) {
                     return null;
@@ -256,13 +300,13 @@
                         // That one is expensive...
                         cacheEntry = methodMap.find(methodKey);
                         if (cacheEntry != null) {
-                            cache.put(methodKey, cacheEntry);
+                            methods.put(methodKey, cacheEntry);
                         } else {
-                            cache.put(methodKey, CACHE_MISS);
+                            methods.put(methodKey, CACHE_MISS);
                         }
                     } catch (MethodKey.AmbiguousException ae) {
                         // that's a miss :-)
-                        cache.put(methodKey, CACHE_MISS);
+                        methods.put(methodKey, CACHE_MISS);
                         throw ae;
                     }
                 }
@@ -283,8 +327,8 @@
                 // cache from defined class towards java.lang.Object because
                 // abstract methods in superclasses would else overwrite 
concrete
                 // classes further down the hierarchy.
-                if (cache.get(methodKey) == null) {
-                    cache.put(methodKey, method);
+                if (methods.get(methodKey) == null) {
+                    methods.put(methodKey, method);
                     methodMap.add(method);
                 }
             }

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/internal/introspection/IntrospectorBase.java
 Thu Jan  7 18:21:29 2010
@@ -19,6 +19,7 @@
 
 import java.lang.reflect.Method;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
 import java.util.Map;
 import java.util.HashMap;
 import java.util.List;
@@ -110,8 +111,34 @@
     }
     // CSON: RedundantThrows
 
+
+    /**
+     * Gets the field named by <code>key</code> for the class <code>c</code>.
+     *
+     * @param c     Class in which the field search is taking place
+     * @param key   Name of the field being searched for
+     * @return the desired field or null if it does not exist or is not 
accessible
+     * */
+    public Field getField(Class<?> c, String key) {
+        ClassMap classMap = getMap(c);
+        return classMap.findField(c, key);
+    }
+
+    /**
+     * Gets the array of accessible field names known for a given class.
+     * @param c the class
+     * @return the class field names
+     */
+    public String[] getFieldNames(Class<?> c) {
+        if (c == null) {
+            return new String[0];
+        }
+        ClassMap classMap = getMap(c);
+        return classMap.getFieldNames();
+    }
+
     /**
-     * Gets the accessible methods names known for a given class.
+     * Gets the array of accessible methods names known for a given class.
      * @param c the class
      * @return the class method names
      */

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/Uberspect.java
 Thu Jan  7 18:21:29 2010
@@ -40,8 +40,8 @@
      * Returns a class constructor.
      * @param ctorHandle a class or class name
      * @param args constructor arguments
-     * @param info template info
-     * @return a {...@link Constructor}.
+     * @param info contextual information
+     * @return a {...@link Constructor}
      */
     Constructor<?> getConstructor(Object ctorHandle, Object[] args, JexlInfo 
info);
     /**
@@ -49,18 +49,18 @@
      * @param obj the object
      * @param method the method name
      * @param args method arguments
-     * @param info template info
-     * @return a {...@link JexlMethod}.
+     * @param info contextual information
+     * @return a {...@link JexlMethod}
      */
     JexlMethod getMethod(Object obj, String method, Object[] args, JexlInfo 
info);
 
     /**
      * Property getter.
      * <p>Returns JexlPropertyGet appropos for ${bar.woogie}.
-     * @param obj the object to get the property from.
+     * @param obj the object to get the property from
      * @param identifier property name
-     * @param info template info
-     * @return a {...@link JexlPropertyGet}.
+     * @param info contextual information
+     * @return a {...@link JexlPropertyGet}
      */
     JexlPropertyGet getPropertyGet(Object obj, Object identifier, JexlInfo 
info);
 
@@ -69,17 +69,17 @@
      * <p>returns JelPropertySet appropos for ${foo.bar = "geir"}</p>.
      * @param obj the object to get the property from.
      * @param identifier property name
-     * @param arg value to set.
-     * @param info template info
+     * @param arg value to set
+     * @param info contextual information
      * @return a {...@link JexlPropertySet}.
      */
     JexlPropertySet getPropertySet(Object obj, Object identifier, Object arg, 
JexlInfo info);
 
     /**
      * Gets an iterator from an object.
-     * @param obj to get the iterator for.
-     * @param info some info.
-     * @return an iterator over obj.
+     * @param obj to get the iterator for
+     * @param info contextual information
+     * @return an iterator over obj
      */
     Iterator<?> getIterator(Object obj, JexlInfo info);
 

Modified: 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
 (original)
+++ 
commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl2/introspection/UberspectImpl.java
 Thu Jan  7 18:21:29 2010
@@ -19,6 +19,7 @@
 import org.apache.commons.jexl2.internal.Introspector;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
 import java.util.Enumeration;
 import java.util.Iterator;
 
@@ -46,19 +47,13 @@
      * Publicly exposed special failure object returned by tryInvoke.
      */
     public static final Object TRY_FAILED = AbstractExecutor.TRY_FAILED;
-    /**
-     * Whether public fields can be considered as properties.
-     */
-    protected final boolean publicProperties;
     
     /**
      * Creates a new UberspectImpl.
      * @param runtimeLogger the logger used for all logging needs
-     * @param publicFields whether public fields should be considered as 
properties
      */
-    public UberspectImpl(Log runtimeLogger, boolean publicFields) {
+    public UberspectImpl(Log runtimeLogger) {
         super(runtimeLogger);
-        publicProperties = publicFields;
     }
 
     /**
@@ -96,6 +91,18 @@
     }
 
     /**
+     * Returns a class field.
+     * @param obj the object
+     * @param name the field name
+     * @param info debug info
+     * @return a {...@link Field}.
+     */
+    public Field getField(Object obj, String name, JexlInfo info) {
+        final Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : 
obj.getClass();
+        return getField(clazz, name);
+    }
+
+    /**
      * {...@inheritdoc}
      */
     public Constructor<?> getConstructor(Object ctorHandle, Object[] args, 
JexlInfo info) {
@@ -110,26 +117,6 @@
     }
 
     /**
-     * Gets a field by name from a class.
-     * @param clazz the class to find the field in
-     * @param name the field name
-     * @return the field instance or null if it could not be found
-     */
-    protected static Field getField(Class<?> clazz, String name) {
-        try {
-            Field field = clazz.getField(name);
-            if (!field.isAccessible()) {
-                field.setAccessible(true);
-            }
-            return field;
-        } catch (NoSuchFieldException xnsf) {
-            return null;
-        } catch (SecurityException xsec) {
-            return null;
-        }
-    }
-
-    /**
      * A JexlPropertyGet for public fields.
      */
     public static final class FieldPropertyGet implements JexlPropertyGet {
@@ -187,9 +174,8 @@
      */
     public JexlPropertyGet getPropertyGet(Object obj, Object identifier, 
JexlInfo info) {
         JexlPropertyGet get = getGetExecutor(obj, identifier);
-        if (get == null && publicProperties && obj != null && identifier != 
null) {
-            Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : 
obj.getClass();
-            Field field = getField(clazz, identifier.toString());
+        if (get == null && obj != null && identifier != null) {
+            Field field = getField(obj, identifier.toString(), info);
             if (field != null) {
                 return new FieldPropertyGet(field);
             }
@@ -259,10 +245,10 @@
      */
     public JexlPropertySet getPropertySet(final Object obj, final Object 
identifier, Object arg, JexlInfo info) {
         JexlPropertySet set = getSetExecutor(obj, identifier, arg);
-        if (set == null && publicProperties && obj != null && identifier != 
null) {
-            Class<?> clazz = obj instanceof Class<?>? (Class<?>) obj : 
obj.getClass();
-            Field field = getField(clazz, identifier.toString());
+        if (set == null && obj != null && identifier != null) {
+            Field field = getField(obj, identifier.toString(), info);
             if (field != null
+                && !Modifier.isFinal(field.getModifiers())
                 && (arg == null || 
MethodKey.isInvocationConvertible(field.getType(), arg.getClass(), false))) {
                 return new FieldPropertySet(field);
             }

Modified: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/IssuesTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/IssuesTest.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/IssuesTest.java
 (original)
+++ 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/IssuesTest.java
 Thu Jan  7 18:21:29 2010
@@ -19,6 +19,7 @@
 import org.apache.commons.jexl2.internal.Introspector;
 import java.util.HashMap;
 import java.util.Map;
+import org.apache.commons.jexl2.introspection.UberspectImpl;
 
 /**
  * Test cases for reported issues
@@ -42,6 +43,7 @@
 
     // JEXL-48: bad assignment detection
     public static class Another {
+        public String name = "whatever";
         private Boolean foo = Boolean.TRUE;
 
         public Boolean foo() {
@@ -157,6 +159,16 @@
             }
         }
         assertTrue("should have foo & goo", found == 2);
+
+        names = ((UberspectImpl) uber).getFieldNames(Another.class);
+        assertTrue("should find fields", names.length > 0);
+        found = 0;
+        for (String name : names) {
+            if ("name".equals(name)) {
+                found += 1;
+            }
+        }
+        assertTrue("should have name", found == 1);
     }
 
     // JEXL-10/JEXL-11: variable checking, null operand is error

Modified: 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java
URL: 
http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java?rev=896952&r1=896951&r2=896952&view=diff
==============================================================================
--- 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java
 (original)
+++ 
commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl2/PublicFieldsTest.java
 Thu Jan  7 18:21:29 2010
@@ -48,8 +48,6 @@
     private JexlContext ctxt;
 
     public PublicFieldsTest() {
-        // create an Uberspect that considers public fields as properties
-        super(new JexlEngine(new 
UberspectImpl(LogFactory.getLog(PublicFieldsTest.class), true), null, null, 
null));
         JEXL.setLenient(false);
     }
 


Reply via email to