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 }