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    }