001    // Copyright 2006, 2007, 2008 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 org.apache.tapestry5.ioc.services.*;
018    import org.apache.tapestry5.ioc.util.BodyBuilder;
019    import org.apache.tapestry5.ioc.util.StrategyRegistry;
020    
021    import java.lang.reflect.Modifier;
022    import java.util.Map;
023    
024    public class StrategyBuilderImpl implements StrategyBuilder
025    {
026        private final ClassFactory classFactory;
027    
028        public StrategyBuilderImpl(@Builtin ClassFactory classFactory)
029        {
030            this.classFactory = classFactory;
031        }
032    
033        public <S> S build(StrategyRegistry<S> registry)
034        {
035            Class<S> interfaceClass = registry.getAdapterType();
036    
037            // TODO: Could cache the implClass by interfaceClass ...
038    
039            Class implClass = createImplClass(interfaceClass);
040    
041            try
042            {
043                Object raw = implClass.getConstructors()[0].newInstance(registry);
044    
045                return interfaceClass.cast(raw);
046            }
047            catch (Exception ex)
048            {
049                throw new RuntimeException(ex);
050            }
051        }
052    
053        public <S> S build(Class<S> adapterType, Map<Class, S> registrations)
054        {
055            StrategyRegistry<S> registry = StrategyRegistry.newInstance(adapterType, registrations);
056    
057            return build(registry);
058        }
059    
060        private Class createImplClass(Class interfaceClass)
061        {
062            ClassFab cf = classFactory.newClass(interfaceClass);
063    
064            String interfaceClassName = interfaceClass.getName();
065    
066            cf.addField("_registry", Modifier.PRIVATE | Modifier.FINAL, StrategyRegistry.class);
067            cf.addConstructor(new Class[]
068                    {StrategyRegistry.class}, null, "_registry = $1;");
069    
070            BodyBuilder builder = new BodyBuilder();
071    
072            MethodIterator mi = new MethodIterator(interfaceClass);
073    
074            while (mi.hasNext())
075            {
076                MethodSignature sig = mi.next();
077    
078                // TODO: Checks for methods that don't have at least one parameter, or don't have a
079                // compatible 1st parameter. Currently, we'll get a Javassist exception.
080    
081                builder.clear();
082                builder.begin();
083    
084                builder.addln("Object selector = $1;");
085                builder.addln(
086                        "%s adapter = (%<s) _registry.getByInstance(selector);",
087                        interfaceClassName);
088                builder.addln("return ($r) adapter.%s($$);", sig.getName());
089    
090                builder.end();
091    
092                cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
093            }
094    
095            if (!mi.getToString())
096                cf.addToString(String.format("<Strategy for %s>", interfaceClassName));
097    
098            return cf.createClass();
099        }
100    }