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

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