You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2003/02/01 17:46:42 UTC

cvs commit: jakarta-tapestry/framework/src/net/sf/tapestry/enhance DefaultComponentClassEnhancer.java

hlship      2003/02/01 08:46:42

  Modified:    framework/src/net/sf/tapestry/enhance
                        DefaultComponentClassEnhancer.java
  Log:
  Create properties for connected parameters if needed.
  
  Revision  Changes    Path
  1.3       +249 -54   jakarta-tapestry/framework/src/net/sf/tapestry/enhance/DefaultComponentClassEnhancer.java
  
  Index: DefaultComponentClassEnhancer.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/net/sf/tapestry/enhance/DefaultComponentClassEnhancer.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- DefaultComponentClassEnhancer.java	24 Jan 2003 03:47:17 -0000	1.2
  +++ DefaultComponentClassEnhancer.java	1 Feb 2003 16:46:42 -0000	1.3
  @@ -69,6 +69,8 @@
   import net.sf.tapestry.IResourceResolver;
   import net.sf.tapestry.Tapestry;
   import net.sf.tapestry.spec.ComponentSpecification;
  +import net.sf.tapestry.spec.Direction;
  +import net.sf.tapestry.spec.ParameterSpecification;
   import net.sf.tapestry.spec.PropertySpecification;
   import org.apache.bcel.Constants;
   import org.apache.bcel.classfile.JavaClass;
  @@ -186,7 +188,7 @@
       {
           Class result = _resolver.findClass(className);
   
  -        if (needsEnhancement(specification))
  +        if (needsEnhancement(result, specification))
               result = createEnhancedSubclass(result, specification);
   
           return result;
  @@ -195,13 +197,81 @@
       /**
        *  Examines the specification, identifies if any enhancements will be needed.
        *  This implementation looks for the presence of any
  -     *  {@link net.sf.tapestry.spec.PropertySpecification}s.
  +     *  {@link net.sf.tapestry.spec.PropertySpecification}s, or any
  +     *  connected parameters where the property is missing or abstract.
        * 
        **/
   
  -    protected boolean needsEnhancement(ComponentSpecification specification)
  +    protected boolean needsEnhancement(Class componentClass, ComponentSpecification specification)
       {
  -        return !specification.getPropertySpecificationNames().isEmpty();
  +        if (!specification.getPropertySpecificationNames().isEmpty())
  +            return true;
  +
  +        if (checkConnectedParameters(componentClass, specification))
  +            return true;
  +
  +        return false;
  +    }
  +
  +    private boolean checkConnectedParameters(
  +        Class componentClass,
  +        ComponentSpecification specification)
  +    {
  +        List names = specification.getParameterNames();
  +        int count = names.size();
  +
  +        Map beanProperties = getBeanProperties(componentClass);
  +
  +        for (int i = 0; i < count; i++)
  +        {
  +            String name = (String) names.get(i);
  +            ParameterSpecification pspec = specification.getParameter(name);
  +
  +            if (checkConnectedParameter(beanProperties, pspec))
  +                return true;
  +        }
  +
  +        return false;
  +    }
  +
  +    /**
  +     *  Returns true if the parameter is connected (has a non-custom direction) and the
  +     *  corresponding property is either missing or abstract.
  +     * 
  +     **/
  +
  +    private boolean checkConnectedParameter(Map beanProperties, ParameterSpecification spec)
  +    {
  +        Direction direction = spec.getDirection();
  +
  +        if (direction == Direction.CUSTOM)
  +            return false;
  +
  +        String propertyName = spec.getPropertyName();
  +
  +        PropertyDescriptor d = (PropertyDescriptor) beanProperties.get(propertyName);
  +
  +        // No existing property matches, so we'll return true to
  +        // create an enhanced class with the property.
  +
  +        boolean readAbstract = isAbstract(d.getReadMethod());
  +
  +        boolean writeAbstract = isAbstract(d.getWriteMethod());
  +
  +        return readAbstract && writeAbstract;
  +    }
  +
  +    /**
  +     *  Returns true if the method is null, or is abstract.
  +     * 
  +     **/
  +
  +    private boolean isAbstract(Method m)
  +    {
  +        if (m == null)
  +            return true;
  +
  +        return Modifier.isAbstract(m.getModifiers());
       }
   
       private Class createEnhancedSubclass(Class startClass, ComponentSpecification specification)
  @@ -218,6 +288,8 @@
           String startClassName = startClass.getName();
           String enhancedName = startClassName + "$Enhance_" + _uid++;
   
  +        Map beanProperties = getBeanProperties(startClass);
  +
           ClassFabricator cf = new ClassFabricator(enhancedName, startClassName);
   
           cf.addDefaultConstructor();
  @@ -231,7 +303,19 @@
   
               PropertySpecification ps = specification.getPropertySpecification(name);
   
  -            createProperty(cf, startClass, ps);
  +            createSpecifiedProperty(cf, startClass, beanProperties, ps);
  +        }
  +
  +        names = specification.getParameterNames();
  +        count = names.size();
  +
  +        for (int i = 0; i < count; i++)
  +        {
  +            String name = (String) names.get(i);
  +
  +            ParameterSpecification ps = specification.getParameter(name);
  +
  +            createParameterProperty(cf, startClass, beanProperties, name, ps);
           }
   
           JavaClass jc = cf.commit();
  @@ -245,23 +329,96 @@
       }
   
       /**
  -     *  Invoked to create the specified property.  Checks that the superclass provides
  -     *  either abstract accessors or none at all.  Creates the file, creates 
  -     *  the accessors, creates initialization code.
  +     *  Invoked to create the specified property. 
        * 
        **/
   
  -    protected void createProperty(ClassFabricator cf, Class startClass, PropertySpecification ps)
  +    protected void createSpecifiedProperty(
  +        ClassFabricator cf,
  +        Class beanClass,
  +        Map beanProperties,
  +        PropertySpecification ps)
       {
           String propertyName = ps.getName();
   
           if (LOG.isDebugEnabled())
  -            LOG.debug("Establishing property " + propertyName);
  +            LOG.debug("Establishing specified property " + propertyName);
  +
  +        createProperty(
  +            cf,
  +            beanClass,
  +            beanProperties,
  +            propertyName,
  +            ps.getType(),
  +            ps.isPersistent());
  +    }
  +
  +    /**
  +     *  Creates a property from a parameter specdification.
  +     * 
  +     **/
  +
  +    protected void createParameterProperty(
  +        ClassFabricator cf,
  +        Class beanClass,
  +        Map beanProperties,
  +        String parameterName,
  +        ParameterSpecification ps)
  +    {
  +        if (ps.getDirection() == Direction.CUSTOM)
  +            return;
  +
  +        String propertyName = ps.getPropertyName();
  +
  +        if (propertyName == null)
  +            propertyName = parameterName;
  +
  +        // Yes, but does it *need* a property created?
   
  -        String type = ps.getType();
  +        if (!isMissingParameterProperty(beanClass, beanProperties, propertyName))
  +            return;
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug("Establishing parameter property " + propertyName);
  +
  +        createProperty(cf, beanClass, beanProperties, propertyName, ps.getType(), false);
  +    }
  +
  +    protected boolean isMissingParameterProperty(
  +        Class beanClass,
  +        Map beanProperties,
  +        String propertyName)
  +    {
  +        PropertyDescriptor pd = (PropertyDescriptor) beanProperties.get(propertyName);
  +
  +        if (pd == null)
  +            return true;
  +
  +        return isAbstract(pd.getReadMethod()) && isAbstract(pd.getWriteMethod());
  +
  +        // Degenerate case:  one accessor abstract, the other real!  Not handled
  +        // here.
  +    }
  +
  +    /**
  +     *  Checks that the superclass provides
  +     *  either abstract accessors or none at all.  Creates the file, creates 
  +     *  the accessors, creates initialization code.
  +     * 
  +     **/
  +
  +    protected void createProperty(
  +        ClassFabricator cf,
  +        Class beanClass,
  +        Map beanProperties,
  +        String propertyName,
  +        String type,
  +        boolean persistent)
  +    {
           Class propertyType = convertPropertyType(type);
   
  -        checkAccessors(startClass, propertyName, propertyType);
  +        String readMethodName =
  +            checkAccessors(beanClass, beanProperties, propertyName, propertyType);
   
           String fieldName = "_$" + propertyName;
   
  @@ -269,17 +426,19 @@
   
           cf.addField(fieldType, fieldName);
   
  -        createAccessor(cf, fieldType, fieldName, propertyName);
  -        createMutator(cf, fieldType, fieldName, propertyName, ps.isPersistent());
  +        createAccessor(cf, fieldType, fieldName, propertyName, readMethodName);
  +        createMutator(cf, fieldType, fieldName, propertyName, persistent);
       }
   
       protected void createAccessor(
           ClassFabricator cf,
           Type fieldType,
           String fieldName,
  -        String propertyName)
  +        String propertyName,
  +        String readMethodName)
       {
  -        String methodName = buildMethodName("get", propertyName);
  +        String methodName =
  +            readMethodName == null ? buildMethodName("get", propertyName) : readMethodName;
   
           MethodFabricator mf = cf.createMethod(Constants.ACC_PUBLIC, fieldType, methodName);
   
  @@ -390,13 +549,64 @@
           return result;
       }
   
  -    protected void checkAccessors(Class startClass, String propertyName, Class propertyType)
  +    /**
  +     *  Checks to see that that class either doesn't provide the property, or does
  +     *  but the accessor(s) are abstract.  Returns the name of the read accessor,
  +     *  or null if there is no such accessor (this is helpful if the beanClass
  +     *  defines a boolean property, where the name of the accessor may be isXXX or
  +     *  getXXX).
  +     * 
  +     **/
  +
  +    protected String checkAccessors(
  +        Class beanClass,
  +        Map beanProperties,
  +        String propertyName,
  +        Class propertyType)
  +    {
  +        PropertyDescriptor d = (PropertyDescriptor) beanProperties.get(propertyName);
  +
  +        if (d == null)
  +            return null;
  +
  +        if (!d.getPropertyType().equals(propertyType))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "DefaultComponentClassEnhancer.property-type-mismatch",
  +                    new Object[] {
  +                        beanClass.getName(),
  +                        propertyName,
  +                        d.getPropertyType().getName(),
  +                        propertyType.getName()}));
  +
  +        Method m = d.getWriteMethod();
  +
  +        if (!isAbstract(m))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "DefaultComponentClassEnhancer.non-abstract-write",
  +                    m.getDeclaringClass().getName(),
  +                    propertyName));
  +
  +        m = d.getReadMethod();
  +
  +        if (!isAbstract(m))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "DefaultComponentClassEnhancer.non-abstract-read",
  +                    m.getDeclaringClass().getName(),
  +                    propertyName));
  +
  +        return m == null ? null : m.getName();
  +    }
  +
  +    protected BeanInfo getBeanInfo(Class beanClass)
       {
  -        BeanInfo info = null;
  +        BeanInfo result = null;
   
           try
           {
  -            info = Introspector.getBeanInfo(startClass);
  +            result = Introspector.getBeanInfo(beanClass);
   
           }
           catch (IntrospectionException ex)
  @@ -404,47 +614,32 @@
               throw new ApplicationRuntimeException(
                   Tapestry.getString(
                       "DefaultComponentClassEnhancer.unable-to-introspect-class",
  -                    startClass.getName()),
  +                    beanClass.getName()),
                   ex);
           }
   
  +        return result;
  +    }
  +
  +    /**
  +     *  Examines the specified class and returns a Map, keyed on propertyName
  +     *  of {@link java.beans.PropertyDescriptor}.
  +     * 
  +     **/
  +
  +    protected Map getBeanProperties(Class beanClass)
  +    {
  +        BeanInfo info = getBeanInfo(beanClass);
  +
  +        Map result = new HashMap();
  +
           PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
   
           for (int i = 0; i < descriptors.length; i++)
           {
  -            PropertyDescriptor d = descriptors[i];
  -
  -            if (!d.getName().equals(propertyName))
  -                continue;
  -
  -            if (!d.getPropertyType().equals(propertyType))
  -                throw new ApplicationRuntimeException(
  -                    Tapestry.getString(
  -                        "DefaultComponentClassEnhancer.property-type-mismatch",
  -                        new Object[] {
  -                            startClass.getName(),
  -                            propertyName,
  -                            d.getPropertyType().getName(),
  -                            propertyType.getName()}));
  -
  -            Method m = d.getReadMethod();
  -
  -            if (m != null && !Modifier.isAbstract(m.getModifiers()))
  -                throw new ApplicationRuntimeException(
  -                    Tapestry.getString(
  -                        "DefaultComponentClassEnhancer.non-abstract-read",
  -                        m.getDeclaringClass().getName(),
  -                        propertyName));
  -
  -            m = d.getWriteMethod();
  -
  -            if (m != null && !Modifier.isAbstract(m.getModifiers()))
  -                throw new ApplicationRuntimeException(
  -                    Tapestry.getString(
  -                        "DefaultComponentClassEnhancer.non-abstract-write",
  -                        m.getDeclaringClass().getName(),
  -                        propertyName));
  -            return;
  +            result.put(descriptors[i].getName(), descriptors[i]);
           }
  +
  +        return result;
       }
   }