// 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();
}
});
}
}