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
015package org.apache.tapestry5.ioc.internal.services;
016
017import java.lang.reflect.Array;
018import java.lang.reflect.Method;
019import java.util.List;
020
021import org.apache.tapestry5.ioc.services.Builtin;
022import org.apache.tapestry5.ioc.services.ChainBuilder;
023import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
024import org.apache.tapestry5.plastic.ClassInstantiator;
025import org.apache.tapestry5.plastic.Condition;
026import org.apache.tapestry5.plastic.InstructionBuilder;
027import org.apache.tapestry5.plastic.InstructionBuilderCallback;
028import org.apache.tapestry5.plastic.PlasticClass;
029import org.apache.tapestry5.plastic.PlasticClassTransformer;
030import org.apache.tapestry5.plastic.PlasticField;
031import org.apache.tapestry5.plastic.WhenCallback;
032
033public 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}