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 }