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
015 package org.apache.hivemind.lib.chain;
016
017 import java.lang.reflect.Modifier;
018 import java.util.HashMap;
019 import java.util.List;
020 import java.util.Map;
021
022 import org.apache.hivemind.ClassResolver;
023 import org.apache.hivemind.service.BodyBuilder;
024 import org.apache.hivemind.service.ClassFab;
025 import org.apache.hivemind.service.ClassFabUtils;
026 import org.apache.hivemind.service.ClassFactory;
027 import org.apache.hivemind.service.MethodIterator;
028 import org.apache.hivemind.service.MethodSignature;
029 import org.apache.hivemind.util.ConstructorUtils;
030 import 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 */
038 public 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 }