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/03/17 14:42:50 UTC

cvs commit: jakarta-tapestry/framework/src/org/apache/tapestry/enhance ComponentClassFactory.java

hlship      2003/03/17 05:42:50

  Modified:    framework/src/org/apache/tapestry/enhance
                        ComponentClassFactory.java
  Log:
  Correct for CVS hickup (revision 1.3 was corrupt).
  
  Revision  Changes    Path
  1.5       +293 -1    jakarta-tapestry/framework/src/org/apache/tapestry/enhance/ComponentClassFactory.java
  
  Index: ComponentClassFactory.java
  ===================================================================
  RCS file: /home/cvs/jakarta-tapestry/framework/src/org/apache/tapestry/enhance/ComponentClassFactory.java,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- ComponentClassFactory.java	17 Mar 2003 03:25:18 -0000	1.4
  +++ ComponentClassFactory.java	17 Mar 2003 13:42:50 -0000	1.5
  @@ -378,4 +378,296 @@
   
       /**
        *  Checks to see that that class either doesn't provide the property, or does
  -     *  but the accessor(s) are abstract.
  \ No newline at end of file
  +     *  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(String propertyName, Class propertyType)
  +    {
  +        PropertyDescriptor d = getPropertyDescriptor(propertyName);
  +
  +        if (d == null)
  +            return null;
  +
  +        if (!d.getPropertyType().equals(propertyType))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "ComponentClassFactory.property-type-mismatch",
  +                    new Object[] {
  +                        _componentClass.getName(),
  +                        propertyName,
  +                        d.getPropertyType().getName(),
  +                        propertyType.getName()}));
  +
  +        Method m = d.getWriteMethod();
  +
  +        if (!isAbstract(m))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "ComponentClassFactory.non-abstract-write",
  +                    m.getDeclaringClass().getName(),
  +                    propertyName));
  +
  +        m = d.getReadMethod();
  +
  +        if (!isAbstract(m))
  +            throw new ApplicationRuntimeException(
  +                Tapestry.getString(
  +                    "ComponentClassFactory.non-abstract-read",
  +                    m.getDeclaringClass().getName(),
  +                    propertyName));
  +
  +        return m == null ? null : m.getName();
  +    }
  +
  +    /**
  +     *  Given an arbitrary type, figures out the correct
  +     *  argument type (for fireObservedChange()) to use.
  +     * 
  +     **/
  +
  +    protected Type convertToArgumentType(Type type)
  +    {
  +        if (type instanceof BasicType)
  +            return type;
  +
  +        return Type.OBJECT;
  +    }
  +
  +    protected void createMutator(
  +        Type fieldType,
  +        String fieldName,
  +        String propertyName,
  +        boolean isPersistent)
  +    {
  +        String methodName = buildMethodName("set", propertyName);
  +
  +        MethodFabricator mf = _classFabricator.createMethod(methodName);
  +        mf.addArgument(fieldType, propertyName);
  +
  +        InstructionList il = mf.getInstructionList();
  +        InstructionFactory factory = _classFabricator.getInstructionFactory();
  +
  +        il.append(factory.createThis());
  +        il.append(factory.createLoad(fieldType, 1));
  +        il.append(factory.createPutField(_subclassName, fieldName, fieldType));
  +
  +        // Persistent properties must invoke fireObservedChange
  +
  +        if (isPersistent)
  +        {
  +            il.append(factory.createThis());
  +            il.append(new PUSH(_classFabricator.getConstantPool(), propertyName));
  +            il.append(factory.createLoad(fieldType, 1));
  +
  +            Type argumentType = convertToArgumentType(fieldType);
  +
  +            il.append(
  +                factory.createInvoke(
  +                    _subclassName,
  +                    "fireObservedChange",
  +                    Type.VOID,
  +                    new Type[] { Type.STRING, argumentType },
  +                    Constants.INVOKEVIRTUAL));
  +        }
  +
  +        il.append(InstructionConstants.RETURN);
  +
  +        mf.commit();
  +    }
  +
  +    protected void createAccessor(
  +        Type fieldType,
  +        String fieldName,
  +        String propertyName,
  +        String readMethodName)
  +    {
  +        String methodName =
  +            readMethodName == null ? buildMethodName("get", propertyName) : readMethodName;
  +
  +        MethodFabricator mf =
  +            _classFabricator.createMethod(Constants.ACC_PUBLIC, fieldType, methodName);
  +
  +        InstructionList il = mf.getInstructionList();
  +        InstructionFactory factory = _classFabricator.getInstructionFactory();
  +
  +        il.append(factory.createThis());
  +        il.append(factory.createGetField(_subclassName, fieldName, fieldType));
  +        il.append(factory.createReturn(fieldType));
  +
  +        mf.commit();
  +    }
  +
  +    /**
  +       *  Checks that the superclass provides
  +       *  either abstract accessors or none at all.  Creates the file, creates 
  +       *  the accessors, creates initialization code.
  +       * 
  +       **/
  +
  +    protected void createProperty(
  +        String propertyName,
  +        String type,
  +        boolean persistent,
  +        Location location)
  +    {
  +        Class propertyType = convertPropertyType(type, location);
  +
  +        String readMethodName = checkAccessors(propertyName, propertyType);
  +
  +        String fieldName = "_$" + propertyName;
  +
  +        Type fieldType = getObjectType(type);
  +
  +        _classFabricator.addField(fieldType, fieldName);
  +
  +        createAccessor(fieldType, fieldName, propertyName, readMethodName);
  +        createMutator(fieldType, fieldName, propertyName, persistent);
  +    }
  +
  +    protected boolean isMissingProperty(String propertyName)
  +    {
  +        PropertyDescriptor pd = getPropertyDescriptor(propertyName);
  +
  +        return isAbstract(pd);
  +    }
  +
  +    /**
  +     *  Creates a property for a connected
  +     *  parameter from a parameter specification.
  +     * 
  +     **/
  +
  +    protected void createConnectedParameterProperty(ParameterSpecification ps)
  +    {
  +        if (ps.getDirection() == Direction.CUSTOM)
  +            return;
  +
  +        String propertyName = ps.getPropertyName();
  +
  +        // Yes, but does it *need* a property created?
  +
  +        if (!isMissingProperty(propertyName))
  +            return;
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug("Establishing connected parameter property " + propertyName);
  +
  +        createProperty(propertyName, ps.getType(), false, ps.getLocation());
  +    }
  +
  +    /**
  +     *  Invoked to create a specified property. 
  +     * 
  +     **/
  +
  +    protected void createSpecifiedProperty(PropertySpecification ps)
  +    {
  +        String propertyName = ps.getName();
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug("Establishing specified property " + propertyName);
  +
  +        createProperty(propertyName, ps.getType(), ps.isPersistent(), ps.getLocation());
  +    }
  +
  +    /**
  +     *  Invoked by {@link org.apache.tapestry.enhance.DefaultComponentClassEnhancer} to
  +     *  create, as a {@link org.apache.bcel.classfile.JavaClass}, an enahanced
  +     *  subclass of the component class.  This means creating a default constructor,
  +     *  new fields, and new accessor and mutator methods.  Properties are created
  +     *  for connected parameters, for all formal parameters (the binding property),
  +     *  and for all specified parameters (which may be transient or persistent).
  +     * 
  +     **/
  +
  +    public JavaClass createEnhancedSubclass()
  +    {
  +        String startClassName = _componentClass.getName();
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug(
  +                "Enhancing subclass of "
  +                    + startClassName
  +                    + " for "
  +                    + _specification.getSpecificationLocation());
  +
  +        _subclassName = startClassName + "$Enhance_" + _uid++;
  +
  +        _classFabricator = new ClassFabricator(_subclassName, startClassName);
  +
  +        _classFabricator.addDefaultConstructor();
  +
  +        createPropertySpecificationEnhancements();
  +
  +        createParameterEnhancements();
  +
  +        JavaClass result = _classFabricator.commit();
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug("Finished creating enhanced class " + _subclassName);
  +
  +        return result;
  +    }
  +
  +    private void createParameterBindingProperty(String parameterName, Location location)
  +    {
  +        String propertyName = parameterName + Tapestry.PARAMETER_PROPERTY_NAME_SUFFIX;
  +
  +        if (!isMissingProperty(propertyName))
  +            return;
  +
  +        if (LOG.isDebugEnabled())
  +            LOG.debug("Establishing parameter binding property " + propertyName);
  +
  +        createProperty(propertyName, IBinding.class.getName(), false, location);
  +    }
  +
  +    /**
  +     *  Creates any properties related to
  +     *  {@link org.apache.tapestry.spec.PropertySpecification property specifications}.
  +     * 
  +     **/
  +
  +    protected void createPropertySpecificationEnhancements()
  +    {
  +        List names = _specification.getPropertySpecificationNames();
  +        int count = names.size();
  +
  +        for (int i = 0; i < count; i++)
  +        {
  +            String name = (String) names.get(i);
  +
  +            PropertySpecification ps = _specification.getPropertySpecification(name);
  +
  +            createSpecifiedProperty(ps);
  +        }
  +    }
  +
  +    /**
  +     *  Creates new properties related to formal parameters.  This is one
  +     *  property to store the binding, and a second property if the parameter
  +     *  is connected.
  +     * 
  +     **/
  +
  +    protected void createParameterEnhancements()
  +    {
  +        List names = _specification.getParameterNames();
  +        int count = names.size();
  +
  +        for (int i = 0; i < count; i++)
  +        {
  +            String name = (String) names.get(i);
  +
  +            ParameterSpecification ps = _specification.getParameter(name);
  +
  +            createParameterBindingProperty(name, ps.getLocation());
  +
  +            createConnectedParameterProperty(ps);
  +        }
  +    }
  +}
  \ No newline at end of file