You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by Christian Meder <ch...@absolutegiganten.org> on 2005/11/14 08:24:40 UTC

Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

On Sun, 2005-11-13 at 05:30 +0000, laurieh@apache.org wrote:
> Author: laurieh
> Date: Sat Nov 12 21:30:23 2005
> New Revision: 332894
> 
> URL: http://svn.apache.org/viewcvs?rev=332894&view=rev
> Log:
> iEnhancing DyanActionForm and friends to remove the need for '.map' when
> accessing properties in JSPs, as described in #36794:
> 
> http://issues.apache.org/bugzilla/show_bug.cgi?id=36794
> 
> 
> Modified:
>     struts/build/trunk/project.xml
>     struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
>     struts/core/trunk/xdocs/userGuide/building_controller.xml
> 
> Modified: struts/build/trunk/project.xml
> URL: http://svn.apache.org/viewcvs/struts/build/trunk/project.xml?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/build/trunk/project.xml (original)
> +++ struts/build/trunk/project.xml Sat Nov 12 21:30:23 2005
> @@ -361,6 +361,16 @@
>      </dependency>
>      
>      <dependency>
> +      <groupId>cglib</groupId>
> +      <artifactId>cglib-nodep</artifactId>
> +      <version>2.1_3</version>
> +      <url>http://cglib.sourceforge.net/</url>
> +      <properties>
> +        <war.bundle>true</war.bundle>
> +      </properties>
> +    </dependency>
> +
> +    <dependency>
>        <groupId>oro</groupId>
>        <artifactId>oro</artifactId>
>        <version>2.0.8</version>
> 
> Modified: struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
> URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java (original)
> +++ struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java Sat Nov 12 21:30:23 2005
> @@ -22,13 +22,24 @@
>  
>  import java.io.Serializable;
>  import java.util.HashMap;
> +import java.util.Map;
> +import java.lang.reflect.Method;
>  
>  import org.apache.commons.beanutils.DynaBean;
>  import org.apache.commons.beanutils.DynaClass;
>  import org.apache.commons.beanutils.DynaProperty;
> +import org.apache.commons.logging.Log;
> +import org.apache.commons.logging.LogFactory;
>  import org.apache.struts.config.FormBeanConfig;
>  import org.apache.struts.config.FormPropertyConfig;
>  import org.apache.struts.util.RequestUtils;
> +import net.sf.cglib.proxy.InterfaceMaker;
> +import net.sf.cglib.proxy.Enhancer;
> +import net.sf.cglib.proxy.MethodInterceptor;
> +import net.sf.cglib.proxy.MethodProxy;
> +import net.sf.cglib.asm.Type;
> +import net.sf.cglib.core.Signature;
> +import net.sf.cglib.core.Constants;
>  
> 
>  /**
> @@ -45,6 +56,7 @@
>  
>  public class DynaActionFormClass implements DynaClass, Serializable {
>  
> +    private Log log = LogFactory.getLog(DynaActionFormClass.class);
>  
>      // ----------------------------------------------------------- Constructors
>  
> @@ -181,10 +193,9 @@
>      public DynaBean newInstance()
>          throws IllegalAccessException, InstantiationException {
>  
> -        DynaActionForm dynaBean =
> -            (DynaActionForm) getBeanClass().newInstance();
> -        dynaBean.setDynaActionFormClass(this);
>          FormPropertyConfig[] props = config.findFormPropertyConfigs();
> +        DynaActionForm dynaBean = (DynaActionForm) doCreate(props);
> +        dynaBean.setDynaActionFormClass(this);
>          for (int i = 0; i < props.length; i++) {
>              dynaBean.set(props[i].getName(), props[i].initial());
>          }
> @@ -312,4 +323,112 @@
>      }
>  
> 
> +    // -------------------------------------------------------- Private Methods
> +
> +    private Object doCreate(FormPropertyConfig[] props) {
> +        // Build an interface to implement consisting of getter/setter
> +        // pairs for each property. Also create a lookup table so we
> +        // can map method names back to the corresponding dynamic
> +        // property on invocation. This allows us to correctly handle
> +        // property names that don't comply with JavaBeans naming
> +        // conventions.
> +        Map properties = new HashMap(props.length * 2);
> +        InterfaceMaker im = new InterfaceMaker();
> +        for (int i = 0; i < props.length; i++) {
> +            String name = props[i].getName();
> +            Class type = props[i].getTypeClass();
> +            Type ttype = Type.getType(type);
> +
> +            if (! name.matches("[\\w]+")) {
> +                // Note: this allows leading digits, which is not legal
> +                // for an identifier but is valid in a getter/setter
> +                // method name. Since you can define such getter/setter
> +                // directly, we support doing so dynamically too.
> +                if (log.isWarnEnabled()) {
> +                    log.warn(
> +                        "Dyna property name '" + name +
> +                        "' in form bean " + config.getName() +
> +                        " is not a legal Java identifier. " +
> +                        "No property access methods generated.");
> +                }
> +            } else {
> +                // Capitalize property name appropriately
> +                String property;
> +                if ((name.length() <= 1) ||

Hmm. Should we handle a name of length "0" ? If we get an empty string
I'd prefer an error instead of continuing.

> +                    (  Character.isLowerCase(name.charAt(0)) &&
> +                    (! Character.isLowerCase(name.charAt(1))))
> +                ) {
> +                    property = name;
> +                } else {
> +                    property =
> +                        Character.toUpperCase(name.charAt(0)) +
> +                        name.substring(1);
> +                }
> +
> +                // Create the getter/setter method pair
> +                Signature getter = new Signature("get"+property, ttype, Constants.TYPES_EMPTY);
> +                Signature setter = new Signature("set"+property, Type.VOID_TYPE, new Type[] { ttype });
> +                im.add(getter, Constants.TYPES_EMPTY);
> +                im.add(setter, Constants.TYPES_EMPTY);
> +                properties.put("get"+property, name);
> +                properties.put("set"+property, name);
> +            }
> +        }
> +        Class beanInterface = im.create();
> +
> +        // Now generate a proxy for the dyna bean that also implements
> +        // the getter/setter methods defined above. We turn off the
> +        // Factory interface to preven problems with BeanUtils.copyProperties

s/preven/prevent

> +        // when both source and target bean are enhanced (otherwise, the
> +        // target bean's callbacks get overwritten with the source beans,
> +        // leading to unexpected behaviour).
> +        Enhancer e = new Enhancer();
> +        e.setSuperclass(beanClass);
> +        e.setInterfaces(new Class[] { beanInterface });
> +        e.setCallback(new BeanInterceptor(properties));
> +        e.setUseFactory(false);
> +
> +        // Return the generated/enhanced bean
> +        return e.create();
> +    }
> +
> +    private static class BeanInterceptor implements MethodInterceptor, Serializable {
> +        private Map propertyLookup;
> +
> +        public BeanInterceptor(Map propertyLookup) {
> +            this.propertyLookup = propertyLookup;
> +        }
> +
> +        public Object intercept(Object obj, Method method, Object[] args,
> +                                MethodProxy proxy) throws Throwable {
> +
> +            String methodNm = method.getName();
> +            String propertyNm = (String) propertyLookup.get(methodNm);
> +            String targetNm = methodNm.substring(0, 3); // get/set
> +
> +            if (propertyNm == null) {
> +                // Not a dyna property access, just pass call along
> +                return proxy.invokeSuper(obj, args);
> +            }
> +
> +            // Handle the dyna property access:
> +            //  - map getFoo(...) to get("foo", ...)
> +            //  - map setFoo(bar, ...) to set("foo", bar, ...)
> +            Class[] targetArgTypes = new Class[args.length + 1];
> +            Object[] targetArgs = new Object[args.length + 1];
> +            targetArgTypes[0] = String.class; // for property name
> +            targetArgs[0] = propertyNm;
> +
> +            System.arraycopy(args, 0, targetArgs, 1, args.length);
> +
> +            for (int i = 0; i < args.length; i++) {
> +                targetArgTypes[i + 1] = args[i].getClass();
> +            }
> +
> +            Method target = obj.getClass().getMethod(targetNm, targetArgTypes);
> +
> +            // Return the result of mapped get/set
> +            return target.invoke(obj, targetArgs);
> +        }
> +    }
>  }
> 
> Modified: struts/core/trunk/xdocs/userGuide/building_controller.xml
> URL: http://svn.apache.org/viewcvs/struts/core/trunk/xdocs/userGuide/building_controller.xml?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/core/trunk/xdocs/userGuide/building_controller.xml (original)
> +++ struts/core/trunk/xdocs/userGuide/building_controller.xml Sat Nov 12 21:30:23 2005

Oh. It's one of that evil "Mix white space cleanup with doc updates
making life harder for reviewers" ;-)




					Christian

-- 
Christian Meder, email: chris@absolutegiganten.org

The Way-Seeking Mind of a tenzo is actualized 
by rolling up your sleeves.

                (Eihei Dogen Zenji)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Christian Meder wrote:
> Hmm. Should we handle a name of length "0" ? If we get an empty string
> I'd prefer an error instead of continuing.

I deliberately avoided making this an error because, whether it makes 
sense or not ;-), an empty string is currently allowed... Seemed better 
to retain compatibility.

> s/preven/prevent

Doh!

> Oh. It's one of that evil "Mix white space cleanup with doc updates
> making life harder for reviewers" ;-)

Ugg, sorry, thought I'd reverted all those.

L.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org