    001    // Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    // http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.ioc.internal.services;
    016
    017    import java.lang.reflect.Array;
    018    import java.lang.reflect.Method;
    019    import java.util.List;
    020
    021    import org.apache.tapestry5.ioc.services.Builtin;
    022    import org.apache.tapestry5.ioc.services.ChainBuilder;
    023    import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
    024    import org.apache.tapestry5.plastic.ClassInstantiator;
    025    import org.apache.tapestry5.plastic.Condition;
    026    import org.apache.tapestry5.plastic.InstructionBuilder;
    027    import org.apache.tapestry5.plastic.InstructionBuilderCallback;
    028    import org.apache.tapestry5.plastic.PlasticClass;
    029    import org.apache.tapestry5.plastic.PlasticClassTransformer;
    030    import org.apache.tapestry5.plastic.PlasticField;
    031    import org.apache.tapestry5.plastic.WhenCallback;
    032
    033    public class ChainBuilderImpl implements ChainBuilder
    034    {
    035        private final PlasticProxyFactory proxyFactory;
    036
    037        public ChainBuilderImpl(@Builtin
    038        PlasticProxyFactory proxyFactory)
    039        {
    040            this.proxyFactory = proxyFactory;
    041        }
    042
    043        @SuppressWarnings("unchecked")
    044        public <T> T build(final Class<T> commandInterface, List<T> commands)
    045        {
    046            // Jump through some hoops to convert the list into an array of the proper type
    047
    048            Object[] array = (Object[]) Array.newInstance(commandInterface, commands.size());
    049
    050            final Object[] commandsArray = commands.toArray(array);
    051
    052            ClassInstantiator<T> instantiator = proxyFactory.createProxy(commandInterface, new PlasticClassTransformer()
    053            {
    054                public void transform(PlasticClass plasticClass)
    055                {
    056                    PlasticField commandsField = plasticClass.introduceField(commandsArray.getClass(), "commands").inject(
    057                            commandsArray);
    058
    059                    for (Method method : commandInterface.getMethods())
    060                    {
    061                        implementMethod(plasticClass, method, commandsField);
    062                    }
    063
    064                    plasticClass.addToString(String.format("<Command chain of %s>", commandInterface.getName()));
    065                }
    066            });
    067
    068            return instantiator.newInstance();
    069        }
    070
    071        private void implementMethod(PlasticClass plasticClass, final Method method, final PlasticField commandsField)
    072        {
    073            plasticClass.introduceMethod(method).changeImplementation(new InstructionBuilderCallback()
    074            {
    075                public void doBuild(InstructionBuilder builder)
    076                {
    077                    builder.loadThis().getField(commandsField).iterateArray(new InstructionBuilderCallback()
    078                    {
    079                        public void doBuild(InstructionBuilder builder)
    080                        {
    081                            // The command is on the stack; add the elements and invoke the method.
    082
    083                            builder.loadArguments().invoke(method);
    084
    085                            Class returnType = method.getReturnType();
    086
    087                            if (returnType == void.class)
    088                                return;
    089
    090                            final boolean wide = returnType == long.class || returnType == double.class;
    091
    092                            if (wide)
    093                                builder.dupeWide();
    094                            else
    095                                builder.dupe();
    096
    097                            if (returnType == float.class)
    098                            {
    099                                builder.loadConstant(0f).compareSpecial("float");
    100                            }
    101
    102                            if (returnType == long.class)
    103                            {
    104                                builder.loadConstant(0l).compareSpecial("long");
    105                            }
    106
    107                            if (returnType == double.class)
    108                            {
    109                                builder.loadConstant(0d).compareSpecial("double");
    110                            }
    111
    112                            Condition condition = returnType.isPrimitive() ? Condition.NON_ZERO : Condition.NON_NULL;
    113
    114                            builder.when(condition, new WhenCallback()
    115                            {
    116                                public void ifTrue(InstructionBuilder builder)
    117                                {
    118                                    builder.returnResult();
    119                                }
    120
    121                                public void ifFalse(InstructionBuilder builder)
    122                                {
    123                                    if (wide)
    124                                        builder.popWide();
    125                                    else
    126                                        builder.pop();
    127                                }
    128                            });
    129                        }
    130                    });
    131
    132                    builder.returnDefaultValue();
    133                }
    134            });
    135        }
    136    }