KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > avalon > fortress > impl > factory > BCELCodeGenerator


1 /*
2  * Copyright 2003-2004 The Apache Software Foundation
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12  * implied.
13  *
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.avalon.fortress.impl.factory;
19
20 import org.apache.bcel.Constants;
21 import org.apache.bcel.classfile.Field;
22 import org.apache.bcel.classfile.JavaClass;
23 import org.apache.bcel.classfile.Method;
24 import org.apache.bcel.generic.*;
25
26 import java.util.HashSet JavaDoc;
27 import java.util.Set JavaDoc;
28
29 /**
30  * <p>
31  * <code>BCELCodeGenerator</code> creates implementations for the
32  * {@link org.apache.bcel.classfile.Method Method}s and
33  * {@link org.apache.bcel.classfile.Field Field}s needed in creating a
34  * <code>WrapperClass</code>.
35  * </p>
36  *
37  * @author <a HREF="mailto:dev@avalon.apache.org">Avalon Development Team</a>
38  */

39
40 public final class BCELCodeGenerator
41 {
42     //***************************************************************************
43
// Fields
44
//***************************************************************************
45

46     /**
47      * The name of the field holding the wrapped class in the generated
48      * wrapper, e.g.
49      * <pre>
50      * <code>
51      * private final <ClassToWrap> WRAPPED_CLASS_FN;
52      * </code>
53      * </pre>
54      */

55     private static final String JavaDoc WRAPPED_CLASS_FN = "m_wrappedClass";
56
57     /**
58      * The name of the field accessor used to ask the generated wrapper for
59      * the wrapped class instance.
60      */

61     private static final String JavaDoc ACCESSOR_METHOD_NAME = "getWrappedObject";
62
63     /**
64      * The name of the wrapper class to be created.
65      */

66     private String JavaDoc m_wrapperClassName;
67
68     /**
69      * The name of the superclass of the wrapper class to be generated.
70      */

71     private String JavaDoc m_wrapperSuperclassName;
72
73     /**
74      * Class object holding the type of the object we want to create a
75      * wrapper for.
76      */

77     private JavaClass m_classToWrap;
78
79     /**
80      * The {@link org.apache.bcel.generic.Type Type} of the class we want to
81      * create a wrapper for.
82      */

83     private Type m_classToWrapType;
84
85     /**
86      * The {@link org.apache.bcel.generic.ClassGen ClassGen} instance to use for
87      * code generation.
88      */

89     private ClassGen m_classGenerator;
90
91     /**
92      * The {@link org.apache.bcel.generic.ConstantPoolGen ConstantPoolGen}
93      * instance to use for code generation.
94      */

95     private ConstantPoolGen m_constPoolGenerator;
96
97     /**
98      * The {@link org.apache.bcel.generic.InstructionList InstructionList} instance
99      * to use during code generation.
100      */

101     private final InstructionList m_instructionList = new InstructionList();
102
103     /**
104      * The {@link org.apache.bcel.generic.InstructionFactory InstructionFactory} to
105      * use during code gereration.
106      */

107     private InstructionFactory m_instructionFactory;
108
109     /**
110      * Flag indicating whether this instance is already initialized or not.
111      */

112     private boolean m_isInitialized = false;
113
114     /**
115      * Default constructor.
116      */

117     public BCELCodeGenerator()
118     {
119         // Left blank
120
}
121
122     public void init(
123         final String JavaDoc wrapperClassName,
124         final String JavaDoc wrapperSuperclassName,
125         final JavaClass classToWrap,
126         final ClassGen classGenerator )
127         throws IllegalArgumentException JavaDoc
128     {
129         if ( classToWrap == null )
130         {
131             final String JavaDoc message = "Target class must not be <null>.";
132             throw new IllegalArgumentException JavaDoc( message );
133         }
134         if ( classToWrap.isAbstract() || !classToWrap.isClass() )
135         {
136             final String JavaDoc message =
137                 "Target class must neither be abstract nor an interface.";
138             throw new IllegalArgumentException JavaDoc( message );
139         }
140         if ( classGenerator == null )
141         {
142             final String JavaDoc message = "ClassGenerator must not be <null>.";
143             throw new IllegalArgumentException JavaDoc( message );
144         }
145
146         m_wrapperClassName = wrapperClassName;
147         m_wrapperSuperclassName = wrapperSuperclassName;
148         m_classToWrap = classToWrap;
149         m_classToWrapType = new ObjectType( m_classToWrap.getClassName() );
150         m_classGenerator = classGenerator;
151         m_constPoolGenerator = m_classGenerator.getConstantPool();
152         m_instructionFactory =
153             new InstructionFactory( m_classGenerator, m_constPoolGenerator );
154
155         m_isInitialized = true;
156     }
157
158     /**
159      * Create a field declaration of the form
160      * <pre>
161      * <code>
162      * private <ClassToWrap> WRAPPED_CLASS_FN;
163      * </code>
164      * </pre>
165      *
166      * @return Field
167      *
168      * @throws IllegalStateException If this instance is not initialized.
169      */

170     public Field createWrappedClassField() throws IllegalStateException JavaDoc
171     {
172         if ( !isInitialized() )
173         {
174             final String JavaDoc message =
175                 "BCELMethodFieldImplementationGenerator is not initialized.";
176             throw new IllegalStateException JavaDoc( message );
177         }
178
179         final FieldGen fg =
180             new FieldGen(
181                 Constants.ACC_PRIVATE,
182                 m_classToWrapType,
183                 WRAPPED_CLASS_FN,
184                 m_constPoolGenerator );
185
186         return fg.getField();
187     }
188
189     /**
190      * Create the wrapper class' default constructor:
191      * <pre>
192      * <code>
193      * public <wrapperClass>(<classToWrap> classToWrap)
194      * {
195      * this.<WRAPPED_CLASS_FN> = classToWrap;
196      * }
197      * </code>
198      * </pre>
199      *
200      * @return The created default constructor
201      *
202      * @throws IllegalStateException If this instance is not initialized.
203      */

204     public Method createDefaultConstructor() throws IllegalStateException JavaDoc
205     {
206         if ( !isInitialized() )
207         {
208             final String JavaDoc message =
209                 "BCELMethodFieldImplementationGenerator is not initialized.";
210             throw new IllegalStateException JavaDoc( message );
211         }
212         final MethodGen mg =
213             new MethodGen(
214                 Constants.ACC_PUBLIC,
215                 Type.VOID,
216                 new Type[]{m_classToWrapType},
217                 new String JavaDoc[]{"classToWrap"},
218                 "<init>",
219                 m_wrapperClassName,
220                 m_instructionList,
221                 m_constPoolGenerator );
222
223         m_instructionList.append(
224                 InstructionFactory.createLoad( Type.OBJECT, 0 ) );
225         m_instructionList.append(
226             m_instructionFactory.createInvoke(
227                 m_wrapperSuperclassName,
228                 "<init>",
229                 Type.VOID,
230                 Type.NO_ARGS,
231                 Constants.INVOKESPECIAL ) );
232         m_instructionList.append(
233                 InstructionFactory.createLoad( Type.OBJECT, 0 ) );
234         m_instructionList.append(
235                 InstructionFactory.createLoad( Type.OBJECT, 1 ) );
236         m_instructionList.append(
237             m_instructionFactory.createFieldAccess(
238                 m_wrapperClassName,
239                 WRAPPED_CLASS_FN,
240                 m_classToWrapType,
241                 Constants.PUTFIELD ) );
242         m_instructionList.append( InstructionFactory.createReturn( Type.VOID ) );
243         mg.setMaxStack();
244         mg.setMaxLocals();
245
246         return extractMethod( mg );
247     }
248
249     /**
250      * Create a field accessor for the wrapped class instance of the form
251      * <pre>
252      * <code>
253      * public Object <ACCESSOR_METHOD_NAME>()
254      * {
255      * return this.<WRAPPED_CLASS_FN>;
256      * }
257      * </code>
258      * </pre>
259      * @return Method
260      * @throws IllegalStateException
261      */

262     public Method createWrappedClassAccessor() throws IllegalStateException JavaDoc
263     {
264         if ( !isInitialized() )
265         {
266             final String JavaDoc message =
267                 "BCELMethodFieldImplementationGenerator is not initialized.";
268             throw new IllegalStateException JavaDoc( message );
269         }
270
271         final MethodGen mg =
272             new MethodGen(
273                 Constants.ACC_PUBLIC,
274                 Type.OBJECT,
275                 Type.NO_ARGS,
276                 new String JavaDoc[]{
277                 },
278                 ACCESSOR_METHOD_NAME,
279                 m_classToWrap.getClassName(),
280                 m_instructionList,
281                 m_constPoolGenerator );
282
283         m_instructionList.append(
284                 InstructionFactory.createLoad( Type.OBJECT, 0 ) );
285         m_instructionList.append(
286             m_instructionFactory.createFieldAccess(
287                 m_wrapperClassName,
288                 WRAPPED_CLASS_FN,
289                 m_classToWrapType,
290                 Constants.GETFIELD ) );
291         m_instructionList.append(
292                 InstructionFactory.createReturn( Type.OBJECT ) );
293
294         mg.setMaxStack();
295         mg.setMaxLocals();
296
297         return extractMethod( mg );
298     }
299
300     /**
301      * Create a method declaration/definition of the form
302      * <pre>
303      * <code>
304      * public <returnType> <methodName>(<parameterTypes>)
305      * throws <exceptionNames>
306      * {
307      * return this.<WRAPPED_CLASS_FN>.<methodName>(<parameterTypes>);
308      * }
309      * </code>
310      * </pre>
311      *
312      * @param meth The method descriptor
313      *
314      * @return Method The {@link org.apache.bcel.classfile.Method Method}
315      * object representing the created method
316      *
317      * @throws IllegalArgumentException If any of the parameters passed in is null.
318      * @throws IllegalStateException If this instance is not initialized.
319      */

320     public Method createMethodWrapper( MethodDesc meth )
321         throws IllegalArgumentException JavaDoc, IllegalStateException JavaDoc
322     {
323         if ( !isInitialized() )
324         {
325             final String JavaDoc message =
326                 "BCELMethodFieldImplementationGenerator is not initialized.";
327             throw new IllegalStateException JavaDoc( message );
328         }
329         if ( meth.name == null
330             || meth.returnType == null
331             || meth.parameterTypes == null
332             || meth.exceptionNames == null )
333         {
334             final String JavaDoc message = "None of the parameters may be <null>.";
335             throw new IllegalArgumentException JavaDoc( message );
336         }
337
338         final MethodGen mg =
339             new MethodGen(
340                 Constants.ACC_PUBLIC,
341                 meth.returnType,
342                 meth.parameterTypes,
343                 null,
344                 meth.name,
345                 m_wrapperClassName,
346                 m_instructionList,
347                 m_constPoolGenerator );
348
349         // Create throws clause
350
for ( int i = 0; i < meth.exceptionNames.length; i++ )
351         {
352             mg.addException( meth.exceptionNames[i] );
353         }
354
355         // Loading the wrapped class instance onto the stack ...
356
m_instructionList.append(
357                 InstructionFactory.createLoad( Type.OBJECT, 0 ) );
358         m_instructionList.append(
359             m_instructionFactory.createFieldAccess(
360                 m_wrapperClassName,
361                 WRAPPED_CLASS_FN,
362                 m_classToWrapType,
363                 Constants.GETFIELD ) );
364
365         // Loading all parameters onto the stack ...
366
short stackIndex = 1;
367         // Stack index 0 is occupied by the wrapped class instance.
368
for ( int i = 0; i < meth.parameterTypes.length; ++i )
369         {
370             m_instructionList.append(
371                     InstructionFactory.createLoad( meth.parameterTypes[i], stackIndex ) );
372             stackIndex += meth.parameterTypes[i].getSize();
373         }
374
375         findImplementation( meth );
376
377         // Invoking the specified method with the loaded parameters on
378
// the wrapped class instance ...
379
m_instructionList.append(
380             m_instructionFactory.createInvoke(
381                     meth.implementingClassName,
382                     meth.name,
383                     meth.returnType,
384                     meth.parameterTypes,
385                     Constants.INVOKEVIRTUAL ) );
386
387         // Creating return statement ...
388
m_instructionList.append( InstructionFactory.createReturn( meth.returnType ) );
389
390         mg.setMaxStack();
391         mg.setMaxLocals();
392
393         return extractMethod( mg );
394     }
395
396     private void findImplementation( MethodDesc meth )
397     {
398         JavaClass currentClass = m_classToWrap;
399
400         while ( null != currentClass && null == meth.implementingClassName )
401         {
402             Method[] methList = currentClass.getMethods();
403
404             for (int i = 0; i < methList.length; i++)
405             {
406                 boolean isEqual = methList[i].isPublic() && !methList[i].isAbstract();
407                 isEqual = isEqual && methList[i].getName().equals(meth.name);
408                 isEqual = isEqual && methList[i].getReturnType().equals(meth.returnType);
409                 isEqual = isEqual && methList[i].getArgumentTypes().length == meth.parameterTypes.length;
410                 Type[] parameterTypes = methList[i].getArgumentTypes();
411                 for (int j = 0; isEqual && j < parameterTypes.length; j++)
412                 {
413                     isEqual = isEqual && parameterTypes[j].equals(meth.parameterTypes[j]);
414                 }
415
416                 if (isEqual)
417                 {
418                     meth.implementingClassName = currentClass.getClassName();
419                     meth.isFinal = methList[i].isFinal();
420                 }
421             }
422
423             currentClass = currentClass.getSuperClass();
424         }
425
426         if (null == meth.implementingClassName)
427         {
428             throw new IllegalStateException JavaDoc("No concrete class for the requested method: " + meth.toString());
429         }
430     }
431
432     /**
433      * Create a method declaration/definition of the form
434      * <pre>
435      * <code>
436      * public <returnType> <methodName>(<parameterTypes>)
437      * throws <exceptionNames>
438      * {
439      * return this.<WRAPPED_CLASS_FN>.<methodName>(<parameterTypes>);
440      * }
441      * </code>
442      * </pre>
443      *
444      * @param methodToWrap The <code>Method</code> to create a wrapper for.
445      *
446      * @return Method The wrapper method.
447      *
448      * @throws IllegalArgumentException If <code>methodToWrao</code> is null.
449      * @throws IllegalStateException If this instance is not initialized.
450      */

451     public Method createMethodWrapper( final Method methodToWrap )
452         throws IllegalArgumentException JavaDoc, IllegalStateException JavaDoc
453     {
454         if ( methodToWrap == null )
455         {
456             final String JavaDoc message = "Method parameter must not be <null>.";
457             throw new IllegalArgumentException JavaDoc( message );
458         }
459
460         return createMethodWrapper( new MethodDesc( methodToWrap ) );
461     }
462
463     /**
464      * Creates an implementation for the supplied {@link org.apache.bcel.classfile.JavaClass JavaClass}
465      * instance representing an interface.
466      *
467      * @param interfacesToImplement The interfaces we want to create an implementation for
468      * @return Method[] An array of {@link org.apache.bcel.classfile.Method Method}
469      * instances representing the interface implementation.
470      * @throws IllegalArgumentException If <code>interfaceToImplement</code> is <code>null</code>
471      * or does not represent an interface
472      * @throws IllegalStateException If this instance has not been initialized
473      */

474     public Method[] createImplementation( final JavaClass[] interfacesToImplement )
475         throws Exception JavaDoc
476     {
477         if ( interfacesToImplement == null )
478         {
479             final String JavaDoc message = "Interface to implement must not be <null>.";
480             throw new IllegalArgumentException JavaDoc( message );
481         }
482         if ( !isInitialized() )
483         {
484             final String JavaDoc message =
485                 "BCELInterfaceImplementationGenerator is not initialized.";
486             throw new IllegalStateException JavaDoc( message );
487         }
488         final Set JavaDoc gmList = new HashSet JavaDoc();
489
490         final MethodDesc[] interfaceMethods = extractMethods( interfacesToImplement );
491         for ( int i = 0; i < interfaceMethods.length; ++i )
492         {
493             final MethodDesc im = interfaceMethods[i];
494
495             // Skip <clinit> method ...
496
if ( im.name.equals( "<clinit>" ) )
497             {
498                 continue;
499             }
500
501             final Method generatedMethod =
502                 createMethodWrapper( im );
503
504             gmList.add( generatedMethod );
505         }
506
507         return (Method[]) gmList.toArray( new Method[gmList.size()] );
508     }
509
510     /**
511      * Extracts the {@link org.apache.bcel.classfile.Method Method} out of
512      * the supplied {@link org.apache.bcel.generic.MethodGen MethodGen} instance,
513      * clears the {@link org.apache.bcel.generic.InstructionList InstructionList}
514      * and returns the extracted <code>Method</code>.
515      *
516      * @param mg The {@link org.apache.bcel.generic.MethodGen MethodGen} instance
517      * holding the {@link org.apache.bcel.classfile.Method Method} to
518      * extract
519      * @return The extracted {@link org.apache.bcel.classfile.Method Method}
520      */

521     private Method extractMethod( final MethodGen mg )
522     {
523         final Method m = mg.getMethod();
524         m_instructionList.dispose();
525         return m;
526     }
527
528     /**
529      * Has this instance already been initialized?
530      *
531      * @return TRUE, if this instance has already been initialized, FALSE otherwise
532      */

533     private boolean isInitialized()
534     {
535         return m_isInitialized;
536     }
537
538     /**
539      * Extracts the collection of {@link org.apache.bcel.classfile.Method Method}s
540      * declared in the supplied {@link org.apache.bcel.classfile.JavaClass JavaClass}
541      * instance. This instance is supposed to represent an interface.
542      *
543      * @param interfacesToImplement The {@link org.apache.bcel.classfile.JavaClass JavaClass}
544      * instances representing the interfaces we are asking for
545      * its methods.
546      * @return MethodDesc[] The array of {@link org.apache.bcel.classfile.Method Method}s
547      * declared by the interface
548      * @throws IllegalArgumentException If <code>interfaceToImplement</code> does not represent an interface
549      * @throws NullPointerException if the <code>interfaceToImplement</code> is <code>null</code>
550      */

551     static MethodDesc[] extractMethods( final JavaClass interfacesToImplement[] )
552         throws Exception JavaDoc
553     {
554         if ( interfacesToImplement == null )
555         {
556             final String JavaDoc message = "JavaClass[] parameter must not be <null>.";
557             throw new NullPointerException JavaDoc( message );
558         }
559
560         Set JavaDoc methods = new HashSet JavaDoc();
561         for (int x = 0; x < interfacesToImplement.length; x++)
562         {
563             JavaClass iface = interfacesToImplement[x];
564             if ( !iface.isInterface() )
565             {
566                 final String JavaDoc message = "JavaClass parameter must be an interface";
567                 throw new IllegalArgumentException JavaDoc( message );
568             }
569
570             extractMethods( iface, methods );
571         }
572
573         return (MethodDesc[]) methods.toArray( new MethodDesc[]{} );
574     }
575
576     private static final void extractMethods( final JavaClass interfaceToImplement, final Set JavaDoc methods )
577     {
578         Method[] meth = interfaceToImplement.getMethods();
579         for ( int m = 0; m < meth.length; m++ )
580         {
581             MethodDesc desc = new MethodDesc(meth[m]);
582             methods.add( desc );
583         }
584     }
585
586     private static final class MethodDesc
587     {
588         final String JavaDoc name;
589         final Type returnType;
590         final Type[] parameterTypes;
591         String JavaDoc[] exceptionNames;
592         boolean isFinal;
593         String JavaDoc implementingClassName;
594
595         MethodDesc( Method meth )
596         {
597             this(meth.getName(), meth.getReturnType(), meth.getArgumentTypes(),
598                     (null == meth.getExceptionTable() ) ? new String JavaDoc[0] : meth.getExceptionTable().getExceptionNames());
599         }
600
601         MethodDesc(String JavaDoc name, Type returnType, Type[] parameterTypes, String JavaDoc[] exceptionNames)
602         {
603             this.name = name;
604             this.returnType = returnType;
605             this.parameterTypes = parameterTypes;
606             this.exceptionNames = exceptionNames;
607             isFinal = false;
608         }
609
610         public boolean equals(Object JavaDoc o)
611         {
612             MethodDesc other = (MethodDesc)o;
613             boolean isEqual = name.equals(other.name);
614             isEqual = isEqual && returnType.equals(other.returnType);
615             isEqual = isEqual && parameterTypes.length == other.parameterTypes.length;
616
617             for (int i = 0; isEqual && i < parameterTypes.length; i++)
618             {
619                 isEqual = isEqual && parameterTypes[i].equals(other.parameterTypes[i]);
620             }
621
622             return isEqual;
623         }
624
625         public int hashCode()
626         {
627             int hash = name.hashCode();
628             hash >>>= 5;
629             hash ^= returnType.hashCode();
630
631             for (int i = 0; i < parameterTypes.length; i++)
632             {
633                 hash >>>= parameterTypes.length;
634                 hash ^= parameterTypes[i].hashCode();
635             }
636
637             return hash;
638         }
639
640         public String JavaDoc toString()
641         {
642             StringBuffer JavaDoc str = new StringBuffer JavaDoc(returnType.getSignature());
643             str.append( " " ).append( name ).append( "(" );
644
645             for(int i = 0; i < parameterTypes.length; i++)
646             {
647                 str.append(parameterTypes[i].toString());
648                 if (i > 0) str.append(",");
649             }
650
651             str.append("),").append((isFinal) ? "f" : "v");
652
653             return str.toString();
654         }
655     }
656 }
657
Popular Tags