// Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package org.apache.tapestry5.ioc.internal.services; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.util.List; import org.apache.tapestry5.ioc.services.Builtin; import org.apache.tapestry5.ioc.services.ChainBuilder; import org.apache.tapestry5.ioc.services.PlasticProxyFactory; import org.apache.tapestry5.plastic.ClassInstantiator; import org.apache.tapestry5.plastic.Condition; import org.apache.tapestry5.plastic.InstructionBuilder; import org.apache.tapestry5.plastic.InstructionBuilderCallback; import org.apache.tapestry5.plastic.PlasticClass; import org.apache.tapestry5.plastic.PlasticClassTransformer; import org.apache.tapestry5.plastic.PlasticField; import org.apache.tapestry5.plastic.WhenCallback; public class ChainBuilderImpl implements ChainBuilder { private final PlasticProxyFactory proxyFactory; public ChainBuilderImpl(@Builtin PlasticProxyFactory proxyFactory) { this.proxyFactory = proxyFactory; } @Override @SuppressWarnings("unchecked") public <T> T build(final Class<T> commandInterface, List<T> commands) { // Jump through some hoops to convert the list into an array of the proper type Object[] array = (Object[]) Array.newInstance(commandInterface, commands.size()); final Object[] commandsArray = commands.toArray(array); ClassInstantiator<T> instantiator = proxyFactory.createProxy(commandInterface, new PlasticClassTransformer() { @Override public void transform(PlasticClass plasticClass) { PlasticField commandsField = plasticClass.introduceField(commandsArray.getClass(), "commands").inject( commandsArray); for (Method method : commandInterface.getMethods()) { implementMethod(plasticClass, method, commandsField); } plasticClass.addToString(String.format("<Command chain of %s>", commandInterface.getName())); } }); return instantiator.newInstance(); } private void implementMethod(PlasticClass plasticClass, final Method method, final PlasticField commandsField) { plasticClass.introduceMethod(method).changeImplementation(new InstructionBuilderCallback() { @Override public void doBuild(InstructionBuilder builder) { builder.loadThis().getField(commandsField).iterateArray(new InstructionBuilderCallback() { @Override public void doBuild(InstructionBuilder builder) { // The command is on the stack; add the elements and invoke the method. builder.loadArguments().invoke(method); Class returnType = method.getReturnType(); if (returnType == void.class) return; final boolean wide = returnType == long.class || returnType == double.class; if (wide) builder.dupeWide(); else builder.dupe(); if (returnType == float.class) { builder.loadConstant(0f).compareSpecial("float"); } if (returnType == long.class) { builder.loadConstant(0l).compareSpecial("long"); } if (returnType == double.class) { builder.loadConstant(0d).compareSpecial("double"); } Condition condition = returnType.isPrimitive() ? Condition.NON_ZERO : Condition.NON_NULL; builder.when(condition, new WhenCallback() { @Override public void ifTrue(InstructionBuilder builder) { builder.returnResult(); } @Override public void ifFalse(InstructionBuilder builder) { if (wide) builder.popWide(); else builder.pop(); } }); } }); builder.returnDefaultValue(); } }); } }