Package org.apache.tapestry.enhance

Source Code of org.apache.tapestry.enhance.CreateAutoParameterEnhancer

/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in
*    the documentation and/or other materials provided with the
*    distribution.
*
* 3. The end-user documentation included with the redistribution,
*    if any, must include the following acknowledgment:
*       "This product includes software developed by the
*        Apache Software Foundation (http://apache.org/)."
*    Alternately, this acknowledgment may appear in the software itself,
*    if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation", "Tapestry"
*    must not be used to endorse or promote products derived from this
*    software without prior written permission. For written
*    permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
*    or "Tapestry", nor may "Apache" or "Tapestry" appear in their
*    name, without prior written permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED.  IN NO EVENT SHALL THE TAPESTRY CONTRIBUTOR COMMUNITY
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation.  For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/

package org.apache.tapestry.enhance;

import org.apache.bcel.Constants;
import org.apache.bcel.generic.BranchInstruction;
import org.apache.bcel.generic.GOTO;
import org.apache.bcel.generic.IFNONNULL;
import org.apache.bcel.generic.InstructionConstants;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tapestry.ApplicationRuntimeException;
import org.apache.tapestry.IBinding;
import org.apache.tapestry.Tapestry;

/**
*  Creates a synthetic property for a
{@link org.apache.tapestry.spec.Direction#AUTO}
*  parameter.
*
@author Howard Lewis Ship
@version $Id: CreateAutoParameterEnhancer.java,v 1.5 2003/05/15 12:34:54 hlship Exp $
@since 3.0
*
**/

public class CreateAutoParameterEnhancer implements IEnhancer
{
    private static final Log LOG = LogFactory.getLog(CreateAutoParameterEnhancer.class);

    private ComponentClassFactory _factory;
    private String _propertyName;
    private String _parameterName;
    private Type _type;
    private String _typeClassName;
    private String _readMethodName;

    private Type _bindingType;
    private Type _classType;

    public CreateAutoParameterEnhancer(
        ComponentClassFactory factory,
        String propertyName,
        String parameterName,
        Type type,
        String typeClassName,
        String readMethodName)
    {
        _factory = factory;
        _propertyName = propertyName;
        _parameterName = parameterName;
        _type = type;
        _typeClassName = typeClassName;
        _readMethodName = readMethodName;

        _bindingType = factory.getObjectType(IBinding.class.getName());
        _classType = factory.getObjectType(Class.class.getName());
    }

    public void performEnhancement(ComponentClassFactory factory)
    {
        if (LOG.isDebugEnabled())
            LOG.debug("Creating auto property: " + _propertyName);

        ClassFabricator cf = factory.getClassFabricator();

        String readBindingMethodName =
            _factory.buildMethodName(
                "get",
                _parameterName + Tapestry.PARAMETER_PROPERTY_NAME_SUFFIX);

        createReadMethod(cf, readBindingMethodName);
        createWriteMethod(cf, readBindingMethodName);
    }

    private String createParameterTypeField(ClassFabricator cf)
    {
        String fieldName = "$type$" + _parameterName;

        cf.addField(Constants.ACC_PRIVATE | Constants.ACC_STATIC, _classType, fieldName);

        MethodFabricator mf = cf.getStaticInitializerMethod();
        InstructionFactory factory = cf.getInstructionFactory();
        String className = cf.getClassName();
        Type throwableType = _factory.getObjectType(Throwable.class.getName());

        mf.append(factory.createGetStatic(className, fieldName, _classType));

        BranchInstruction ifNotNullBI = new IFNONNULL(null);

        mf.append(ifNotNullBI);

        // Invoke Class.forName and store ther result.
        // Concern: will the class be visible to the right class loader?
        // May need to use alternate forName() and pass Thread's context class loader.

        mf.append(new PUSH(cf.getConstantPool(), _typeClassName));
        InstructionHandle tryStart =
            mf.append(
                factory.createInvoke(
                    "java.lang.Class",
                    "forName",
                    _classType,
                    new Type[] { Type.STRING },
                    Constants.INVOKESTATIC));
        mf.append(factory.createPutStatic(className, fieldName, _classType));

        GOTO jumpOut = new GOTO(null);

        InstructionHandle tryEnd = mf.append(jumpOut);

        String exceptionClassName = ApplicationRuntimeException.class.getName();

        InstructionHandle catchHandle = mf.append(factory.createNew(exceptionClassName));

        // This stuff can make my head spin, so let's map it out a little.
        // CCE = ClassCastException, ARE = ApplicationRuntimeException

        // Stack: CCE, ARE --> ARE, CCE, ARE

        mf.append(InstructionConstants.DUP_X1);

        // Stack: ARE, CCE, ARE -> ARE, ARE, CCE

        mf.append(InstructionConstants.SWAP);

        mf.append(
            factory.createInvoke(
                exceptionClassName,
                Constants.CONSTRUCTOR_NAME,
                Type.VOID,
                new Type[] { throwableType },
                Constants.INVOKESPECIAL));

        mf.append(InstructionConstants.ATHROW);

        mf.addExceptionHandler(
            tryStart,
            tryEnd,
            catchHandle,
            new ObjectType(ClassNotFoundException.class.getName()));

        InstructionHandle end = mf.append(InstructionConstants.NOP);

        ifNotNullBI.setTarget(end);
        jumpOut.setTarget(end);

        return fieldName;
    }

    private void createReadMethod(ClassFabricator cf, String readBindingMethodName)
    {
        String methodName =
            _readMethodName == null
                ? _factory.buildMethodName("get", _propertyName)
                : _readMethodName;

        if (LOG.isDebugEnabled())
            LOG.debug("Creating method: " + methodName);

        Type[] noArgs = new Type[] {
        };

        MethodFabricator mf = cf.createMethod(Constants.ACC_PUBLIC, _type, methodName);

        InstructionFactory factory = cf.getInstructionFactory();

        mf.append(factory.createThis());
        mf.append(
            factory.createInvoke(
                cf.getClassName(),
                readBindingMethodName,
                _bindingType,
                noArgs,
                Constants.INVOKEVIRTUAL));

        String accessMethodName = null;

        if (isBoolean(_type))
            accessMethodName = "getBoolean";
        else
            if (isInt(_type))
                accessMethodName = "getInt";
            else
                if (isDouble(_type))
                    accessMethodName = "getDouble";
                else
                    if (isString(_type))
                        accessMethodName = "getString";

        if (accessMethodName != null)
        {
            // The binding object is on top of the stack
            mf.append(
                factory.createInvoke(
                    IBinding.class.getName(),
                    accessMethodName,
                    _type,
                    noArgs,
                    Constants.INVOKEINTERFACE));
        }
        else
        {
            // Type is either an object type or an array type

            // To invoke getObject(parameterName, type) we need the type.
            // We mimic what Java compiler does; create a private static field
            // to store the type, and add a static initializer that invokes Class.forName.

            String fieldName = createParameterTypeField(cf);

            mf.append(new PUSH(cf.getConstantPool(), _parameterName));
            mf.append(factory.createGetStatic(cf.getClassName(), fieldName, _classType));

            mf.append(
                factory.createInvoke(
                    IBinding.class.getName(),
                    "getObject",
                    Type.OBJECT,
                    new Type[] { Type.STRING, _classType },
                    Constants.INVOKEINTERFACE));

            // ReferenceType is superclass to ObjectType and ArrayType

            mf.append(factory.createCheckCast((ReferenceType) _type));
        }

        mf.append(factory.createReturn(_type));

        mf.commit();
    }

    private void createWriteMethod(ClassFabricator cf, String readBindingMethodName)
    {
        String methodName = _factory.buildMethodName("set", _propertyName);

        if (LOG.isDebugEnabled())
            LOG.debug("Creating method: " + methodName);

        MethodFabricator mf = cf.createMethod(methodName);
        mf.addArgument(_type, _propertyName);

        InstructionFactory factory = cf.getInstructionFactory();

        String updateMethodName = null;
        Type argumentType = _type;

        if (isBoolean(_type))
            updateMethodName = "setBoolean";
        else
            if (isInt(_type))
                updateMethodName = "setInt";
            else
                if (isDouble(_type))
                    updateMethodName = "setDouble";
                else
                    if (isString(_type))
                        updateMethodName = "setString";
                    else
                    {
                        updateMethodName = "setObject";
                        argumentType = Type.OBJECT;
                    }

        // Get the binding

        mf.append(factory.createThis());
        mf
            .append(
                factory
                .createInvoke(cf.getClassName(), readBindingMethodName, _bindingType, new Type[] {
        }, Constants.INVOKEVIRTUAL));

        // Push the parameter value (remember,
        // parameter 0 is "this")

        mf.append(factory.createLoad(_type, 1));

        // Invoke the update method

        mf.append(
            factory.createInvoke(
                IBinding.class.getName(),
                updateMethodName,
                Type.VOID,
                new Type[] { argumentType },
                Constants.INVOKEINTERFACE));

        mf.append(InstructionConstants.RETURN);
        mf.commit();
    }

    private boolean isBoolean(Type t)
    {
        return t.getType() == Constants.T_BOOLEAN;
    }

    private boolean isDouble(Type t)
    {
        return t.getType() == Constants.T_DOUBLE;
    }

    private boolean isInt(Type t)
    {
        return t.getType() == Constants.T_INT;
    }

    private boolean isString(Type t)
    {
        return Type.STRING.equals(t);
    }
}
TOP

Related Classes of org.apache.tapestry.enhance.CreateAutoParameterEnhancer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.