001// Copyright 2005 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
015package org.apache.hivemind.lib.chain;
016
017import java.lang.reflect.Modifier;
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.hivemind.ClassResolver;
023import org.apache.hivemind.service.BodyBuilder;
024import org.apache.hivemind.service.ClassFab;
025import org.apache.hivemind.service.ClassFabUtils;
026import org.apache.hivemind.service.ClassFactory;
027import org.apache.hivemind.service.MethodIterator;
028import org.apache.hivemind.service.MethodSignature;
029import org.apache.hivemind.util.ConstructorUtils;
030import org.apache.hivemind.util.Defense;
031
032/**
033 * Gang of Four Chain of Commands Pattern.
034 * 
035 * @author Howard M. Lewis Ship
036 * @since 1.1
037 */
038public class ChainBuilderImpl implements ChainBuilder
039{
040    private ClassResolver _classResolver;
041
042    private ClassFactory _classFactory;
043
044    /**
045     * Map, keyed on service interface, of Class.
046     */
047
048    private Map _implementations = new HashMap();
049
050    public Object buildImplementation(Class commandInterface, List commands, String toString)
051    {
052        Defense.notNull(commandInterface, "commandInterface");
053        Defense.notNull(commands, "commands");
054        Defense.notNull(toString, "toString");
055
056        Class instanceClass = findImplementationClass(commandInterface);
057
058        return createInstance(instanceClass, commands, toString);
059    }
060
061    private synchronized Class findImplementationClass(Class commandInterface)
062    {
063        Class result = (Class) _implementations.get(commandInterface);
064
065        if (result == null)
066        {
067            result = constructImplementationClass(commandInterface);
068
069            _implementations.put(commandInterface, result);
070        }
071
072        return result;
073    }
074
075    private Class constructImplementationClass(Class commandInterface)
076    {
077        String name = ClassFabUtils.generateClassName(commandInterface);
078
079        ClassFab cf = _classFactory.newClass(name, Object.class);
080
081        addInfrastructure(cf, commandInterface);
082
083        addMethods(cf, commandInterface);
084
085        return cf.createClass();
086    }
087
088    void addInfrastructure(ClassFab cf, Class commandInterface)
089    {
090        Class array = _classResolver.findClass(commandInterface.getName() + "[]");
091
092        cf.addInterface(commandInterface);
093        cf.addField("_commands", array);
094        cf.addField("_toString", String.class);
095
096        BodyBuilder builder = new BodyBuilder();
097
098        builder.begin();
099        builder.addln("_commands = ({0}[]) $1.toArray(new {0}[0]);", commandInterface.getName());
100        builder.addln("_toString = $2;");
101        builder.end();
102
103        cf.addConstructor(new Class[]
104        { List.class, String.class }, null, builder.toString());
105    }
106
107    private Object createInstance(Class instanceClass, List commands, String toString)
108    {
109        return ConstructorUtils.invokeConstructor(instanceClass, new Object[]
110        { commands, toString });
111    }
112
113    void addMethods(ClassFab cf, Class commandInterface)
114    {
115        MethodIterator mi = new MethodIterator(commandInterface);
116
117        while (mi.hasNext())
118        {
119            MethodSignature sig = mi.next();
120
121            addMethod(cf, commandInterface, sig);
122        }
123
124        if (!mi.getToString())
125            addToString(cf);
126    }
127
128    void addMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
129    {
130        Class returnType = sig.getReturnType();
131
132        if (returnType.equals(void.class))
133        {
134            addVoidMethod(cf, commandInterface, sig);
135            return;
136        }
137
138        String defaultValue = defaultForReturnType(returnType);
139
140        BodyBuilder builder = new BodyBuilder();
141        builder.begin();
142
143        builder
144                .addln(
145                        "{0} result = {1};",
146                        ClassFabUtils.getJavaClassName(returnType),
147                        defaultValue);
148        builder.addln("for (int i = 0; i < _commands.length; i++)");
149
150        builder.begin();
151        builder.addln("result = _commands[i].{0}($$);", sig.getName());
152
153        builder.addln("if (result != {0}) break;", defaultValue);
154
155        builder.end();
156
157        builder.addln("return result;");
158        builder.end();
159
160        cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
161    }
162
163    String defaultForReturnType(Class returnType)
164    {
165        // For all object and array types.
166
167        if (!returnType.isPrimitive())
168            return "null";
169
170        if (returnType.equals(boolean.class))
171            return "false";
172
173        // Assume, then, that it is a numeric type (this method
174        // isn't called for void).
175
176        return "0";
177    }
178
179    private void addVoidMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
180    {
181        BodyBuilder builder = new BodyBuilder();
182
183        builder.begin();
184
185        builder.addln("for (int i = 0; i < _commands.length; i++)");
186        builder.addln("_commands[i].{0}($$);", sig.getName());
187
188        builder.end();
189
190        cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
191    }
192
193    void addToString(ClassFab cf)
194    {
195        MethodSignature sig = new MethodSignature(String.class, "toString", null, null);
196
197        cf.addMethod(Modifier.PUBLIC, sig, "return _toString;");
198    }
199
200    public void setClassFactory(ClassFactory classFactory)
201    {
202        _classFactory = classFactory;
203    }
204
205    public void setClassResolver(ClassResolver classResolver)
206    {
207        _classResolver = classResolver;
208    }
209}