Save This Page
Home » xwork-2.1.5 » com.opensymphony » xwork2 » ognl » [javadoc | source]
    1   /*
    2    * Copyright (c) 2002-2006 by OpenSymphony
    3    * All rights reserved.
    4    */
    5   package com.opensymphony.xwork2.ognl;
    6   
    7   import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
    8   import com.opensymphony.xwork2.inject.Inject;
    9   import com.opensymphony.xwork2.util.CompoundRoot;
   10   import com.opensymphony.xwork2.util.logging.Logger;
   11   import com.opensymphony.xwork2.util.logging.LoggerFactory;
   12   import com.opensymphony.xwork2.util.reflection.ReflectionException;
   13   import ognl;
   14   
   15   import java.beans.BeanInfo;
   16   import java.beans.IntrospectionException;
   17   import java.beans.Introspector;
   18   import java.beans.PropertyDescriptor;
   19   import java.lang.reflect.Method;
   20   import java.util.Collection;
   21   import java.util.HashMap;
   22   import java.util.Map;
   23   import java.util.concurrent.ConcurrentHashMap;
   24   
   25   
   26   /**
   27    * Utility class that provides common access to the Ognl APIs for
   28    * setting and getting properties from objects (usually Actions).
   29    *
   30    * @author Jason Carreira
   31    */
   32   public class OgnlUtil {
   33   
   34       private static final Logger LOG = LoggerFactory.getLogger(OgnlUtil.class);
   35       private ConcurrentHashMap<String, Object> expressions = new ConcurrentHashMap<String, Object>();
   36       private final ConcurrentHashMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<Class, BeanInfo>();
   37   
   38       private TypeConverter defaultConverter;
   39       static boolean devMode = false;
   40       static boolean enableExpressionCache = true;
   41   
   42       @Inject
   43       public void setXWorkConverter(XWorkConverter conv) {
   44           this.defaultConverter = new OgnlTypeConverterWrapper(conv);
   45       }
   46   
   47       @Inject("devMode")
   48       public static void setDevMode(String mode) {
   49           devMode = "true".equals(mode);
   50       }
   51   
   52       @Inject("enableOGNLExpressionCache")
   53       public static void setEnableExpressionCache(String cache) {
   54          enableExpressionCache = "true".equals(cache);
   55       }
   56   
   57       /**
   58        * Sets the object's properties using the default type converter, defaulting to not throw
   59        * exceptions for problems setting the properties.
   60        *
   61        * @param props   the properties being set
   62        * @param o       the object
   63        * @param context the action context
   64        */
   65       public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context) {
   66           setProperties(props, o, context, false);
   67       }
   68   
   69       /**
   70        * Sets the object's properties using the default type converter.
   71        *
   72        * @param props                   the properties being set
   73        * @param o                       the object
   74        * @param context                 the action context
   75        * @param throwPropertyExceptions boolean which tells whether it should throw exceptions for
   76        *                                problems setting the properties
   77        */
   78       public void setProperties(Map<String, ?> props, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException{
   79           if (props == null) {
   80               return;
   81           }
   82   
   83           Ognl.setTypeConverter(context, getTypeConverterFromContext(context));
   84   
   85           Object oldRoot = Ognl.getRoot(context);
   86           Ognl.setRoot(context, o);
   87   
   88           for (Map.Entry<String, ?> entry : props.entrySet()) {
   89               String expression = entry.getKey();
   90               internalSetProperty(expression, entry.getValue(), o, context, throwPropertyExceptions);
   91           }
   92   
   93           Ognl.setRoot(context, oldRoot);
   94       }
   95   
   96       /**
   97        * Sets the properties on the object using the default context, defaulting to not throwing
   98        * exceptions for problems setting the properties.
   99        *
  100        * @param properties
  101        * @param o
  102        */
  103       public void setProperties(Map<String, ?> properties, Object o) {
  104           setProperties(properties, o, false);
  105       }
  106   
  107       /**
  108        * Sets the properties on the object using the default context.
  109        *
  110        * @param properties              the property map to set on the object
  111        * @param o                       the object to set the properties into
  112        * @param throwPropertyExceptions boolean which tells whether it should throw exceptions for
  113        *                                problems setting the properties
  114        */
  115       public void setProperties(Map<String, ?> properties, Object o, boolean throwPropertyExceptions) {
  116           Map context = Ognl.createDefaultContext(o);
  117           setProperties(properties, o, context, throwPropertyExceptions);
  118       }
  119   
  120       /**
  121        * Sets the named property to the supplied value on the Object, defaults to not throwing
  122        * property exceptions.
  123        *
  124        * @param name    the name of the property to be set
  125        * @param value   the value to set into the named property
  126        * @param o       the object upon which to set the property
  127        * @param context the context which may include the TypeConverter
  128        */
  129       public void setProperty(String name, Object value, Object o, Map<String, Object> context) {
  130           setProperty(name, value, o, context, false);
  131       }
  132   
  133       /**
  134        * Sets the named property to the supplied value on the Object.
  135        *
  136        * @param name                    the name of the property to be set
  137        * @param value                   the value to set into the named property
  138        * @param o                       the object upon which to set the property
  139        * @param context                 the context which may include the TypeConverter
  140        * @param throwPropertyExceptions boolean which tells whether it should throw exceptions for
  141        *                                problems setting the property
  142        */
  143       public void setProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) {
  144           Ognl.setTypeConverter(context, getTypeConverterFromContext(context));
  145   
  146           Object oldRoot = Ognl.getRoot(context);
  147           Ognl.setRoot(context, o);
  148   
  149           internalSetProperty(name, value, o, context, throwPropertyExceptions);
  150   
  151           Ognl.setRoot(context, oldRoot);
  152       }
  153   
  154       /**
  155        * Looks for the real target with the specified property given a root Object which may be a
  156        * CompoundRoot.
  157        *
  158        * @return the real target or null if no object can be found with the specified property
  159        */
  160       public Object getRealTarget(String property, Map<String, Object> context, Object root) throws OgnlException {
  161           //special keyword, they must be cutting the stack
  162           if ("top".equals(property)) {
  163               return root;
  164           }
  165   
  166           if (root instanceof CompoundRoot) {
  167               // find real target
  168               CompoundRoot cr = (CompoundRoot) root;
  169   
  170               try {
  171                   for (Object target : cr) {
  172                       if (
  173                               OgnlRuntime.hasSetProperty((OgnlContext) context, target, property)
  174                                       ||
  175                                       OgnlRuntime.hasGetProperty((OgnlContext) context, target, property)
  176                                       ||
  177                                       OgnlRuntime.getIndexedPropertyType((OgnlContext) context, target.getClass(), property) != OgnlRuntime.INDEXED_PROPERTY_NONE
  178                               ) {
  179                           return target;
  180                       }
  181                   }
  182               } catch (IntrospectionException ex) {
  183                   throw new ReflectionException("Cannot figure out real target class", ex);
  184               }
  185   
  186               return null;
  187           }
  188   
  189           return root;
  190       }
  191   
  192   
  193       /**
  194        * Wrapper around Ognl.setValue() to handle type conversion for collection elements.
  195        * Ideally, this should be handled by OGNL directly.
  196        */
  197       public void setValue(String name, Map<String, Object> context, Object root, Object value) throws OgnlException {
  198           Ognl.setValue(compile(name), context, root, value);
  199       }
  200   
  201       public Object getValue(String name, Map<String, Object> context, Object root) throws OgnlException {
  202           return Ognl.getValue(compile(name), context, root);
  203       }
  204   
  205       public Object getValue(String name, Map<String, Object> context, Object root, Class resultType) throws OgnlException {
  206           return Ognl.getValue(compile(name), context, root, resultType);
  207       }
  208   
  209   
  210       public Object compile(String expression) throws OgnlException {
  211           if (enableExpressionCache) {
  212               Object o = expressions.get(expression);
  213               if (o == null) {
  214                   o = Ognl.parseExpression(expression);
  215                   expressions.put(expression, o);
  216               }
  217               return o;
  218           } else
  219               return Ognl.parseExpression(expression);
  220       }
  221   
  222       /**
  223        * Copies the properties in the object "from" and sets them in the object "to"
  224        * using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
  225        * is specified.
  226        *
  227        * @param from       the source object
  228        * @param to         the target object
  229        * @param context    the action context we're running under
  230        * @param exclusions collection of method names to excluded from copying ( can be null)
  231        * @param inclusions collection of method names to included copying  (can be null)
  232        *                   note if exclusions AND inclusions are supplied and not null nothing will get copied.
  233        */
  234       public void copy(Object from, Object to, Map<String, Object> context, Collection<String> exclusions, Collection<String> inclusions) {
  235           if (from == null || to == null) {
  236               LOG.warn("Attempting to copy from or to a null source. This is illegal and is bein skipped. This may be due to an error in an OGNL expression, action chaining, or some other event.");
  237   
  238               return;
  239           }
  240   
  241           TypeConverter conv = getTypeConverterFromContext(context);
  242           Map contextFrom = Ognl.createDefaultContext(from);
  243           Ognl.setTypeConverter(contextFrom, conv);
  244           Map contextTo = Ognl.createDefaultContext(to);
  245           Ognl.setTypeConverter(contextTo, conv);
  246   
  247           PropertyDescriptor[] fromPds;
  248           PropertyDescriptor[] toPds;
  249   
  250           try {
  251               fromPds = getPropertyDescriptors(from);
  252               toPds = getPropertyDescriptors(to);
  253           } catch (IntrospectionException e) {
  254               LOG.error("An error occured", e);
  255   
  256               return;
  257           }
  258   
  259           Map<String, PropertyDescriptor> toPdHash = new HashMap<String, PropertyDescriptor>();
  260   
  261           for (PropertyDescriptor toPd : toPds) {
  262               toPdHash.put(toPd.getName(), toPd);
  263           }
  264   
  265           for (PropertyDescriptor fromPd : fromPds) {
  266               if (fromPd.getReadMethod() != null) {
  267                   boolean copy = true;
  268                   if (exclusions != null && exclusions.contains(fromPd.getName())) {
  269                       copy = false;
  270                   } else if (inclusions != null && !inclusions.contains(fromPd.getName())) {
  271                       copy = false;
  272                   }
  273   
  274                   if (copy == true) {
  275                       PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
  276                       if ((toPd != null) && (toPd.getWriteMethod() != null)) {
  277                           try {
  278                               Object expr = compile(fromPd.getName());
  279                               Object value = Ognl.getValue(expr, contextFrom, from);
  280                               Ognl.setValue(expr, contextTo, to, value);
  281                           } catch (OgnlException e) {
  282                               // ignore, this is OK
  283                           }
  284                       }
  285   
  286                   }
  287   
  288               }
  289   
  290           }
  291       }
  292   
  293   
  294       /**
  295        * Copies the properties in the object "from" and sets them in the object "to"
  296        * using specified type converter, or {@link com.opensymphony.xwork2.conversion.impl.XWorkConverter} if none
  297        * is specified.
  298        *
  299        * @param from    the source object
  300        * @param to      the target object
  301        * @param context the action context we're running under
  302        */
  303       public void copy(Object from, Object to, Map<String, Object> context) {
  304           copy(from, to, context, null, null);
  305       }
  306   
  307       /**
  308        * Get's the java beans property descriptors for the given source.
  309        *
  310        * @param source the source object.
  311        * @return property descriptors.
  312        * @throws IntrospectionException is thrown if an exception occurs during introspection.
  313        */
  314       public PropertyDescriptor[] getPropertyDescriptors(Object source) throws IntrospectionException {
  315           BeanInfo beanInfo = getBeanInfo(source);
  316           return beanInfo.getPropertyDescriptors();
  317       }
  318   
  319   
  320       /**
  321        * Get's the java beans property descriptors for the given class.
  322        *
  323        * @param clazz the source object.
  324        * @return property descriptors.
  325        * @throws IntrospectionException is thrown if an exception occurs during introspection.
  326        */
  327       public PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws IntrospectionException {
  328           BeanInfo beanInfo = getBeanInfo(clazz);
  329           return beanInfo.getPropertyDescriptors();
  330       }
  331   
  332       /**
  333        * Creates a Map with read properties for the given source object.
  334        * <p/>
  335        * If the source object does not have a read property (i.e. write-only) then
  336        * the property is added to the map with the value <code>here is no read method for property-name</code>.
  337        *
  338        * @param source the source object.
  339        * @return a Map with (key = read property name, value = value of read property).
  340        * @throws IntrospectionException is thrown if an exception occurs during introspection.
  341        * @throws OgnlException          is thrown by OGNL if the property value could not be retrieved
  342        */
  343       public Map getBeanMap(Object source) throws IntrospectionException, OgnlException {
  344           Map beanMap = new HashMap();
  345           Map sourceMap = Ognl.createDefaultContext(source);
  346           PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source);
  347           for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
  348               String propertyName = propertyDescriptor.getDisplayName();
  349               Method readMethod = propertyDescriptor.getReadMethod();
  350               if (readMethod != null) {
  351                   Object expr = compile(propertyName);
  352                   Object value = Ognl.getValue(expr, sourceMap, source);
  353                   beanMap.put(propertyName, value);
  354               } else {
  355                   beanMap.put(propertyName, "There is no read method for " + propertyName);
  356               }
  357           }
  358           return beanMap;
  359       }
  360   
  361       /**
  362        * Get's the java bean info for the given source object. Calls getBeanInfo(Class c).
  363        *
  364        * @param from the source object.
  365        * @return java bean info.
  366        * @throws IntrospectionException is thrown if an exception occurs during introspection.
  367        */
  368       public BeanInfo getBeanInfo(Object from) throws IntrospectionException {
  369           return getBeanInfo(from.getClass());
  370       }
  371   
  372   
  373       /**
  374        * Get's the java bean info for the given source.
  375        *
  376        * @param clazz the source class.
  377        * @return java bean info.
  378        * @throws IntrospectionException is thrown if an exception occurs during introspection.
  379        */
  380       public BeanInfo getBeanInfo(Class clazz) throws IntrospectionException {
  381           synchronized (beanInfoCache) {
  382               BeanInfo beanInfo;
  383               beanInfo = beanInfoCache.get(clazz);
  384               if (beanInfo == null) {
  385                   beanInfo = Introspector.getBeanInfo(clazz, Object.class);
  386                   beanInfoCache.put(clazz, beanInfo);
  387               }
  388               return beanInfo;
  389           }
  390       }
  391   
  392       void internalSetProperty(String name, Object value, Object o, Map<String, Object> context, boolean throwPropertyExceptions) throws ReflectionException{
  393           try {
  394               setValue(name, context, o, value);
  395           } catch (OgnlException e) {
  396               Throwable reason = e.getReason();
  397               String msg = "Caught OgnlException while setting property '" + name + "' on type '" + o.getClass().getName() + "'.";
  398               Throwable exception = (reason == null) ? e : reason;
  399   
  400               if (throwPropertyExceptions) {
  401                   throw new ReflectionException(msg, exception);
  402               } else {
  403                   if (devMode) {
  404                       LOG.warn(msg, exception);
  405                   }
  406               }
  407           }
  408       }
  409   
  410       TypeConverter getTypeConverterFromContext(Map<String, Object> context) {
  411           /*ValueStack stack = (ValueStack) context.get(ActionContext.VALUE_STACK);
  412           Container cont = (Container)stack.getContext().get(ActionContext.CONTAINER);
  413           if (cont != null) {
  414               return new OgnlTypeConverterWrapper(cont.getInstance(XWorkConverter.class));
  415           } else {
  416               throw new IllegalArgumentException("Cannot find type converter in context map");
  417           }
  418           */
  419           return defaultConverter;
  420       }
  421   }

Save This Page
Home » xwork-2.1.5 » com.opensymphony » xwork2 » ognl » [javadoc | source]