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    }