001    // Copyright 2004, 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.service.impl;
016    
017    import java.lang.reflect.Modifier;
018    import java.util.ArrayList;
019    import java.util.HashMap;
020    import java.util.Iterator;
021    import java.util.List;
022    import java.util.Map;
023    
024    import javassist.CannotCompileException;
025    import javassist.CtClass;
026    import javassist.CtConstructor;
027    import javassist.CtField;
028    import javassist.CtMethod;
029    import javassist.NotFoundException;
030    
031    import org.apache.hivemind.ApplicationRuntimeException;
032    import org.apache.hivemind.service.ClassFab;
033    import org.apache.hivemind.service.MethodFab;
034    import org.apache.hivemind.service.MethodSignature;
035    
036    /**
037     * Implementation of {@link org.apache.hivemind.service.ClassFab}. Hides, as much as possible, the
038     * underlying library (Javassist).
039     * 
040     * @author Howard Lewis Ship
041     */
042    public class ClassFabImpl extends AbstractFab implements ClassFab
043    {
044        /**
045         * Stores information about a constructor; used by toString().
046         * 
047         * @since 1.1
048         */
049    
050        private class AddedConstructor
051        {
052            private Class[] _parameterTypes;
053    
054            private Class[] _exceptionTypes;
055    
056            private String _body;
057    
058            AddedConstructor(Class[] parameterTypes, Class[] exceptionTypes, String body)
059            {
060                _parameterTypes = parameterTypes;
061                _exceptionTypes = exceptionTypes;
062                _body = body;
063            }
064    
065            public String toString()
066            {
067                StringBuffer buffer = new StringBuffer();
068    
069                buffer.append("public ");
070                buffer.append(getCtClass().getName());
071    
072                buffer.append("(");
073    
074                int count = size(_parameterTypes);
075                for (int i = 0; i < count; i++)
076                {
077                    if (i > 0)
078                        buffer.append(", ");
079    
080                    buffer.append(_parameterTypes[i].getName());
081    
082                    buffer.append(" $");
083                    buffer.append(i + 1);
084                }
085    
086                buffer.append(")");
087    
088                count = size(_exceptionTypes);
089                for (int i = 0; i < count; i++)
090                {
091                    if (i == 0)
092                        buffer.append("\n  throws ");
093                    else
094                        buffer.append(", ");
095    
096                    buffer.append(_exceptionTypes[i].getName());
097                }
098    
099                buffer.append("\n");
100                buffer.append(_body);
101    
102                buffer.append("\n");
103    
104                return buffer.toString();
105            }
106    
107            private int size(Object[] array)
108            {
109                return array == null ? 0 : array.length;
110            }
111        }
112    
113        /**
114         * Map of {@link MethodFab}keyed on {@link MethodSignature}.
115         */
116        private Map _methods = new HashMap();
117    
118        /**
119         * List of {@link AddedConstructor}.
120         * 
121         * @since 1.1
122         */
123    
124        private List _constructors = new ArrayList();
125    
126        public ClassFabImpl(CtClassSource source, CtClass ctClass)
127        {
128            super(source, ctClass);
129        }
130    
131        /**
132         * Returns a representation of the fabricated class, including inheritance, fields,
133         * constructors, methods and method bodies.
134         * 
135         * @since 1.1
136         */
137        public String toString()
138        {
139            StringBuffer buffer = new StringBuffer("ClassFab[\n");
140    
141            try
142            {
143                buildClassAndInheritance(buffer);
144    
145                buildFields(buffer);
146    
147                buildConstructors(buffer);
148    
149                buildMethods(buffer);
150    
151            }
152            catch (Exception ex)
153            {
154                buffer.append(" *** ");
155                buffer.append(ex);
156            }
157    
158            buffer.append("\n]");
159    
160            return buffer.toString();
161        }
162    
163        /** @since 1.1 */
164        private void buildMethods(StringBuffer buffer)
165        {
166            Iterator i = _methods.values().iterator();
167            while (i.hasNext())
168            {
169    
170                MethodFab mf = (MethodFab) i.next();
171    
172                buffer.append("\n");
173                buffer.append(mf);
174                buffer.append("\n");
175            }
176        }
177    
178        /** @since 1.1 */
179        private void buildConstructors(StringBuffer buffer)
180        {
181            Iterator i = _constructors.iterator();
182    
183            while (i.hasNext())
184            {
185                buffer.append("\n");
186                buffer.append(i.next());
187            }
188        }
189    
190        /** @since 1.1 */
191        private void buildFields(StringBuffer buffer) throws NotFoundException
192        {
193            CtField fields[] = getCtClass().getDeclaredFields();
194    
195            for (int i = 0; i < fields.length; i++)
196            {
197                buffer.append("\n");
198                buffer.append(modifiers(fields[i].getModifiers()));
199                buffer.append(" ");
200                buffer.append(fields[i].getType().getName());
201                buffer.append(" ");
202                buffer.append(fields[i].getName());
203                buffer.append(";\n");
204            }
205        }
206    
207        /** @since 1.1 */
208        private void buildClassAndInheritance(StringBuffer buffer) throws NotFoundException
209        {
210            buffer.append(modifiers(getCtClass().getModifiers()));
211            buffer.append(" class ");
212            buffer.append(getCtClass().getName());
213            buffer.append(" extends ");
214            buffer.append(getCtClass().getSuperclass().getName());
215            buffer.append("\n");
216    
217            CtClass[] interfaces = getCtClass().getInterfaces();
218    
219            if (interfaces.length > 0)
220            {
221                buffer.append("  implements ");
222    
223                for (int i = 0; i < interfaces.length; i++)
224                {
225                    if (i > 0)
226                        buffer.append(", ");
227    
228                    buffer.append(interfaces[i].getName());
229                }
230    
231                buffer.append("\n");
232            }
233        }
234    
235        private String modifiers(int modifiers)
236        {
237            return Modifier.toString(modifiers);
238        }
239    
240        /**
241         * Returns the name of the class fabricated by this instance.
242         */
243        String getName()
244        {
245            return getCtClass().getName();
246        }
247    
248        public void addField(String name, Class type)
249        {
250            CtClass ctType = convertClass(type);
251    
252            try
253            {
254                CtField field = new CtField(ctType, name, getCtClass());
255                field.setModifiers(Modifier.PRIVATE);
256    
257                getCtClass().addField(field);
258            }
259            catch (CannotCompileException ex)
260            {
261                throw new ApplicationRuntimeException(ServiceMessages.unableToAddField(
262                        name,
263                        getCtClass(),
264                        ex), ex);
265            }
266        }
267    
268        public boolean containsMethod( MethodSignature ms )
269        {
270            return _methods.get( ms ) != null;
271        }
272        
273        public MethodFab addMethod(int modifiers, MethodSignature ms, String body)
274        {
275            if (_methods.get(ms) != null)
276                throw new ApplicationRuntimeException(ServiceMessages.duplicateMethodInClass(ms, this));
277    
278            CtClass ctReturnType = convertClass(ms.getReturnType());
279    
280            CtClass[] ctParameters = convertClasses(ms.getParameterTypes());
281            CtClass[] ctExceptions = convertClasses(ms.getExceptionTypes());
282    
283            CtMethod method = new CtMethod(ctReturnType, ms.getName(), ctParameters, getCtClass());
284    
285            try
286            {
287                method.setModifiers(modifiers);
288                method.setBody(body);
289                method.setExceptionTypes(ctExceptions);
290    
291                getCtClass().addMethod(method);
292            }
293            catch (Exception ex)
294            {
295                throw new ApplicationRuntimeException(ServiceMessages.unableToAddMethod(
296                        ms,
297                        getCtClass(),
298                        ex), ex);
299            }
300    
301            // Return a MethodFab so the caller can add catches.
302    
303            MethodFab result = new MethodFabImpl(getSource(), ms, method, body);
304    
305            _methods.put(ms, result);
306    
307            return result;
308        }
309    
310        public MethodFab getMethodFab(MethodSignature ms)
311        {
312            return (MethodFab) _methods.get(ms);
313        }
314    
315        public void addConstructor(Class[] parameterTypes, Class[] exceptions, String body)
316        {
317            CtClass[] ctParameters = convertClasses(parameterTypes);
318            CtClass[] ctExceptions = convertClasses(exceptions);
319    
320            try
321            {
322                CtConstructor constructor = new CtConstructor(ctParameters, getCtClass());
323                constructor.setExceptionTypes(ctExceptions);
324                constructor.setBody(body);
325    
326                getCtClass().addConstructor(constructor);
327    
328                _constructors.add(new AddedConstructor(parameterTypes, exceptions, body));
329            }
330            catch (Exception ex)
331            {
332                throw new ApplicationRuntimeException(ServiceMessages.unableToAddConstructor(
333                        getCtClass(),
334                        ex), ex);
335            }
336        }
337    
338    }