We noticed the same thing. Maybe we should replace it with JSP-EL? (I don't
have time to rewrite OGNL myself.)

In the mean time, we wrote a custom version of OgnlValueStack (pasted below)
which optimizes and uses reflection for normal Java expressions; it's an
order of magnitude faster. I think it's 100% compatible (we haven't noticed
any problems).

We don't use JSP, so I'm not sure how applicable this would be to other
users. If it is applicable, I'll happily check this in.

Bob

import com.opensymphony.xwork.util.OgnlUtil;
import com.opensymphony.xwork.util.OgnlValueStack;
import ognl.Ognl;
import ognl.OgnlException;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
* Optimizes [EMAIL PROTECTED] #findValue(String, Class)} for expressions that 
are Java
* identifiers. Performances tests indicate this is an order of magnitude
faster
* than [EMAIL PROTECTED] OgnlValueStack}. Unlike [EMAIL PROTECTED] 
OgnlValueStack}, this
* implementation does not eat exceptions when finding values.
*
* @author [EMAIL PROTECTED] (Bob Lee)
*/
public class FastValueStack extends OgnlValueStack {

 static final Object NOT_FOUND = new Object();

 Map<String, String> overrides;
 Class defaultType;

 public FastValueStack(OgnlValueStack vs) {
   super(vs);
 }

 public FastValueStack() {}

 @Override public void setExprOverrides(Map overrides) {
   super.setExprOverrides(overrides);
   this.overrides = overrides;
 }

 @Override public void setDefaultType(Class defaultType) {
   super.setDefaultType(defaultType);
   this.defaultType = defaultType;
 }

 @Override public Object findValue(String expression, Class asType) {
   if (expression == null) {
     return null;
   }

   if (overrides != null && overrides.containsKey(expression)) {
     expression = (String) overrides.get(expression);
   }

   if (isJavaIdentifier(expression)) {
     Object value = findValueForJavaIdentifier(expression, asType);
     if (value != NOT_FOUND) {
       return value;
     }
   }

   // fall back to old behavior.
   return findValueUsingOgnl(expression, asType);
 }

 /**
  * Tries to find a value when the expression is a java identifier. Returns
  * the value or <code>NOT_FOUND</code> if no qualifying map entry or
accessor
  * method is found.
  */
 Object findValueForJavaIdentifier(String javaIdentifier, Class asType) {
   for (Object o : getRoot()) {
     if (o instanceof Map) {
       // gets value from a map.
       Map m = (Map) o;
       if (m.containsKey(javaIdentifier)) {
         Object value = m.get(javaIdentifier);
         if (value == null || isAssignable(asType, value.getClass())) {
           return value;
         }
       }
     } else {
       // gets value by invoking a getter.
       Getter getter = Getter.getInstance(o.getClass(), javaIdentifier);
       if (getter != null
           && isAssignable(asType, getter.getMethod().getReturnType())) {
         return getter.invoke(o);
       }
     }
   }

   return NOT_FOUND;
 }

 Object findValueUsingOgnl(String expression, Class asType) {
   try {
     Object compiled = OgnlUtil.compile(expression);
     return Ognl.getValue(compiled, getContext(), getRoot(), asType);
   } catch (OgnlException e) {
     throw new RuntimeException(e);
   }
 }

 /**
  * Maps primitive classes to their wrapper classes and vice versa.
  */
 static Map<Class, Class> primitiveClassMap = createPrimitiveClassMap();

 static Map<Class, Class> createPrimitiveClassMap() {
   Map<Class, Class> primitiveClassMap = new HashMap<Class, Class>() {{
     put(boolean.class, Boolean.class);
     put(char.class, Character.class);
     put(short.class, Short.class);
     put(int.class, Integer.class);
     put(long.class, Long.class);
     put(float.class, Float.class);
     put(double.class, Double.class);
   }};

   // add inverted mappings.
   Map<Class, Class> inverted = new HashMap<Class, Class>();
   for (Map.Entry<Class, Class> entry : primitiveClassMap.entrySet()) {
     inverted.put(entry.getValue(), entry.getKey());
   }
   primitiveClassMap.putAll(inverted);

   return primitiveClassMap;
 }

 /**
  * Returns true if you can cast <code>from<code> to <code>to</code>.
Allows
  * for assignment between primitives and their respective wrapper types.
  *
  * @param to type to cast to
  * @param from actual type
  */
 static boolean isAssignable(Class to, Class from) {
   if (to.isAssignableFrom(from)) {
     return true;
   }

   Class validFrom = primitiveClassMap.get(to);
   if (validFrom != null && validFrom.equals(from)) {
     return true;
   }

   return false;
 }


 static Map<String, String> javaIdentifiers =
     new ConcurrentHashMap<String, String>();

 /**
  * Returns true if the string is a valid Java identifier.
  */
 static boolean isJavaIdentifier(String s) {
   if (javaIdentifiers.containsKey(s)) {
     return true;
   }

   if (s.length() == 0
       || !Character.isJavaIdentifierStart(s.charAt(0))) {
     return false;
   }

   for (int i = 1; i < s.length(); i++) {
     if (!Character.isJavaIdentifierPart(s.charAt(i))) {
       return false;
     }
   }

   javaIdentifiers.put(s, s);
   return true;
 }
}

import static com.google.common.base.ReferenceType.*;
import com.google.common.base.StringUtil;
import com.google.common.collect.ReferenceCache;

import java.lang.reflect.Method;
import java.util.Map;

/**
* Simplifies invoking getter methods.
*
* @author Bob Lee ([EMAIL PROTECTED])
*/
public class Getter {

 Method method;

 static final Getter NOT_FOUND = new Getter();

 Getter(Class clazz, String propertyName) throws NoSuchMethodException {
   try {
     method = clazz.getMethod(getMethodName("get", propertyName));
   }
   catch (NoSuchMethodException e) {
     // try "isXxx" if "getXxx" doesn't work.
     method = clazz.getMethod(getMethodName("is", propertyName));
   }
 }

 /** Used for NOT_FOUND. */
 Getter() {}

 public Object invoke(Object target) {
   try {
     return method.invoke(target);
   }
   catch (Exception e) {
     throw new RuntimeException(e);
   }
 }

 public Method getMethod() {
   return method;
 }

 static String getMethodName(String prefix, String propertyName) {
   return prefix + StringUtil.capitalize(propertyName);
 }

 static GetterCache getterCache = new GetterCache();

 /**
  * Class -> Property Name -> Getter
  */
 static class GetterCache extends ReferenceCache<Class, Map<String,
Getter>> {

   public GetterCache() {
     super(WEAK, SOFT);
   }

   public Map<String, Getter> create(final Class clazz) {
     return new ReferenceCache<String, Getter>(STRONG, STRONG) {
       public Getter create(String propertyName) {
         try {
           return new Getter(clazz, propertyName);
         } catch (NoSuchMethodException e) {
           return NOT_FOUND;
         }
       }
     };
   }
 }

 /**
  * Looks up a Getter instance. Returns null if a getter method with the
given
  * name is not found.
  */
 public static Getter getInstance(Class clazz, String propertyName) {
   Getter getter = getterCache.get(clazz).get(propertyName);
   return (getter == NOT_FOUND) ? null : getter;
 }
}

Reply via email to