    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    }