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.Method;
018 import java.util.Map;
019
020 import org.apache.tapestry5.ioc.services.Builtin;
021 import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
022 import org.apache.tapestry5.ioc.services.StrategyBuilder;
023 import org.apache.tapestry5.ioc.util.StrategyRegistry;
024 import org.apache.tapestry5.plastic.ClassInstantiator;
025 import org.apache.tapestry5.plastic.InstructionBuilder;
026 import org.apache.tapestry5.plastic.InstructionBuilderCallback;
027 import org.apache.tapestry5.plastic.MethodDescription;
028 import org.apache.tapestry5.plastic.PlasticClass;
029 import org.apache.tapestry5.plastic.PlasticClassTransformer;
030 import org.apache.tapestry5.plastic.PlasticField;
031
032 public class StrategyBuilderImpl implements StrategyBuilder
033 {
034 private final PlasticProxyFactory proxyFactory;
035
036 public StrategyBuilderImpl(@Builtin
037 PlasticProxyFactory proxyFactory)
038 {
039 this.proxyFactory = proxyFactory;
040 }
041
042 public <S> S build(StrategyRegistry<S> registry)
043 {
044 return createProxy(registry.getAdapterType(), registry);
045 }
046
047 public <S> S build(Class<S> adapterType, Map<Class, S> registrations)
048 {
049 StrategyRegistry<S> registry = StrategyRegistry.newInstance(adapterType, registrations);
050
051 return build(registry);
052 }
053
054 private <S> S createProxy(final Class<S> interfaceType, final StrategyRegistry<S> registry)
055 {
056 ClassInstantiator instantiator = proxyFactory.createProxy(interfaceType, new PlasticClassTransformer()
057 {
058 public void transform(PlasticClass plasticClass)
059 {
060 final PlasticField registryField = plasticClass.introduceField(StrategyRegistry.class, "registry")
061 .inject(registry);
062
063 for (final Method method : interfaceType.getMethods())
064 {
065 plasticClass.introduceMethod(new MethodDescription(method), new InstructionBuilderCallback()
066 {
067 public void doBuild(InstructionBuilder builder)
068 {
069 Class returnType = method.getReturnType();
070
071 builder.loadThis().getField(registryField);
072
073 // Argument 0 is the selector used to find the adapter and should be an object reference,
074 // not a primitive.
075
076 builder.loadArgument(0);
077
078 // Use the StrategyRegistry to get the adapter to re-invoke the method on
079 builder.invoke(StrategyRegistry.class, Object.class, "getByInstance", Object.class)
080 .checkcast(interfaceType);
081
082 // That leaves the correct adapter on top of the stack. Get the
083 // selector and the rest of the arguments in place and invoke the method.
084
085 builder.loadArguments().invoke(interfaceType, returnType, method.getName(),
086 method.getParameterTypes());
087
088 builder.returnResult();
089 }
090 });
091 }
092
093 plasticClass.addToString(String.format("<Strategy for %s>", interfaceType.getName()));
094 }
095 });
096
097 return interfaceType.cast(instantiator.newInstance());
098 }
099 }