KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > fractal > julia > asm > InterceptorClassGenerator


1 /***
2  * Julia: France Telecom's implementation of the Fractal API
3  * Copyright (C) 2001-2002 France Telecom R&D
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  *
19  * Contact: Eric.Bruneton@rd.francetelecom.com
20  *
21  * Author: Eric Bruneton
22  */

23
24 package org.objectweb.fractal.julia.asm;
25
26 import org.objectweb.fractal.api.control.ContentController;
27 import org.objectweb.fractal.julia.InitializationContext;
28 import org.objectweb.fractal.julia.Interceptor;
29 import org.objectweb.fractal.julia.loader.Initializable;
30 import org.objectweb.fractal.julia.loader.Loader;
31 import org.objectweb.fractal.julia.loader.Tree;
32
33 import org.objectweb.asm.CodeVisitor;
34 import org.objectweb.asm.Type;
35 import org.objectweb.asm.ClassReader;
36 import org.objectweb.asm.ClassAdapter;
37 import org.objectweb.asm.ClassVisitor;
38 import org.objectweb.asm.Attribute;
39 import org.objectweb.asm.CodeAdapter;
40
41 import java.lang.reflect.Method JavaDoc;
42 import java.util.ArrayList JavaDoc;
43 import java.util.List JavaDoc;
44 import java.util.Arrays JavaDoc;
45 import java.util.Map JavaDoc;
46 import java.util.HashMap JavaDoc;
47 import java.io.IOException JavaDoc;
48
49 /**
50  * A class generator to generate {@link Interceptor} classes. This
51  * generator produces sub classes of a given class that implement a given set
52  * of interfaces by adding interception code to the original code of each
53  * method (this original code just forwards method calls to the {@link
54  * Interceptor#getFcItfDelegate getFcItfDelegate} object). The interception
55  * code is generated by {@link CodeGenerator} and/or {@link ClassTransformer}
56  * objects.
57  * <p>
58  * The original code of each method is visited and adapted on the fly by the
59  * code adapters returned by the code generators (see {@link CodeGenerator}),
60  * resulting in a modified code that includes the interception
61  * code generated by each code generator.
62  * <p>
63  * For example, if the original code <tt>void m () { impl.m() }</tt> is modified
64  * by the code adapter returned by a code generator A into:
65  * <pre>
66  * void m () {
67  * <font color=red>// pre code A
68  * try {</font>
69  * impl.m();
70  * <font color=red>} finally {
71  * // post code A
72  * }</font>
73  * }
74  * </pre>
75  * and by the code adapter returned by a code generator B into:
76  * <pre>
77  * void m () {
78  * <font color=green>// pre code B</font>
79  * impl.m();
80  * <font color=green>// post code B</font>
81  * }
82  * </pre>
83  * then the original code will be modified by A and B, in this order (the last
84  * code adapter is applied first), into:
85  * <pre>
86  * void m () {
87  * <font color=red>// pre code A
88  * try {</font>
89  * <font color=green>// pre code B</font>
90  * impl.m();
91  * <font color=green>// post code B</font>
92  * <font color=red>} finally {
93  * // post code A
94  * }</font>
95  * }
96  * </pre>
97  * If the original code is modified by B and A, the result is:
98  * <pre>
99  * void m () {
100  * <font color=green>// pre code B</font>
101  * <font color=red>// pre code A
102  * try {</font>
103  * impl.m();
104  * <font color=red>} finally {
105  * // post code A
106  * }</font>
107  * <font color=green>// post code B</font>
108  * }
109  * </pre>
110  *
111  * The complete class generated for an interface <tt>I</tt> containing only the
112  * above <tt>m</tt> method, and with A = {@link LifeCycleCodeGenerator} and B =
113  * {@link TraceCodeGenerator}, in this order, is the following (if there is more
114  * than one interface, some additional casts are used - as in the {@link
115  * InterfaceClassGenerator} class):
116  * <pre>
117  * public class <i>XYZ</i>
118  * implements I, Interceptor, Generated
119  * {
120  *
121  * private I impl;<font color=red>
122  * private <i>UVW</i> lc;</font>
123  *
124  * public <i>XYZ</i> (Object impl) {
125  * setFcItfDelegate(impl);
126  * }
127  *
128  * public void m () {
129  * <font color=red>synchronized(lc) {
130  * if (lc.fcState != 2)
131  * lc.incrementFcInvocationCounter();
132  * else
133  * lc.fcInvocationCounter++;
134  * }
135  * try {</font><font color=green>
136  * System.err.println("Entering public abstract void I.m()");</font>
137  * impl.m();<font color=green>
138  * System.err.println("Leaving public abstract void I.m()");</font>
139  * <font color=red>} finally {
140  * synchronized (lc) {
141  * if (lc.fcState != 2)
142  * lc.decrementFcInvocationCounter();
143  * else
144  * lc.fcInvocationCounter--;
145  * }
146  * }</font>
147  * }
148  *
149  * // overriden Controller methods
150  *
151  * public void initFcController (InitializationContext ic) {<font color=red>
152  * lc = (<i>UVW</i>)ic.getInterface("lifecycle-controller");</font>
153  * }
154  *
155  * // Interceptor methods
156  *
157  * public Object getFcItfDelegate () {
158  * return impl;
159  * }
160  *
161  * public void setFcItfDelegate (Object o) {
162  * impl = (I)o;
163  * }
164  *
165  * public Object clone () {
166  * <i>XYZ</i> clone = new <i>XYZ</i>(impl);<font color=red>
167  * clone.lc = lc;</font>
168  * return clone;
169  * }
170  *
171  * // Generated methods
172  *
173  * public String getFcGeneratorParameters () {
174  * return "((...InterceptorClassGenerator ...LifeCycleCodeGenerator
175  * ...TraceCodeGenerator) java.lang.Object (I) (...) in)";
176  * }
177  * }
178  * </pre>
179  * This class generator can be used in two different ways, corresponding to
180  * the {@link ContextClassGenerator#generateClass optimization levels} of
181  * components:
182  * <ul>
183  * <li><tt>none</tt>, <tt>mergedControllers</tt> or
184  * <tt>mergeControllersAndContent</tt>: in this case the arguments of the
185  * class generator must be of the form "(object-descriptor superClass (itf ...
186  * itf) (controller ... controller) in|out)", with an non empty list of
187  * controller class names.</li>
188  * <li><tt>mergedControllersAndInterceptors</tt> or
189  * <tt>mergeControllersInterceptorsAndContent</tt>: in this case the arguments
190  * of the class generator must be of the form "(object-descriptor superClass
191  * (itf ... itf) () in|out)", i.e. the controller class name list must be empty,
192  * and the super class must be the merged controller class.</li>
193  * </ul>
194  * In the last case the generated code is quite different from the above code:
195  * <ul>
196  * <li>the "impl" field is not generated: the "fcContent" field is used instead.
197  * </li>
198  * <li>the {@link Interceptor} interface is not implemented.</li>
199  * <li>the code generated by the code generators may be different. This is the
200  * case for example with the life cycle code generator: the "lc" field is not
201  * generated, and replaced by <tt>this</tt> in the code (the "fcState" and
202  * "fcInvocationCounter" fields should indeed be defined in the super class in
203  * this case).</li>
204  * </ul>
205  * {@link ClassTransformer ClassTransformers} can be used only with the
206  * <tt>mergeControllersInterceptorsAndContent</tt> option. These transformers
207  * transform the "user component" class, <i>before</i> the interception code is
208  * added by the {@link CodeGenerator CodeGenerators}. If class transformers are
209  * used, the generated class is not generated as a sub class of the given
210  * super class: instead, the code of this super class (and of its own super
211  * class) is directly copied (after tranformation) into the generated class.
212  */

213
214 public class InterceptorClassGenerator
215   extends AbstractClassGenerator
216   implements Initializable
217 {
218
219   /**
220    * Names of the methods that can be defined both in the content class and in
221    * the merged controller class.
222    */

223
224   private final static List JavaDoc FRACTAL_METHODS = Arrays.asList(new String JavaDoc[] {
225     "listFc", "lookupFc", "bindFc", "unbindFc",
226     "getFcState", "startFc", "stopFc"
227   });
228
229   /**
230    * The arguments of the class that is being generated.
231    */

232
233   public Tree args;
234
235   /**
236    * Descriptors of the code generators used by this class generator.
237    */

238
239   public Tree codeGenDescs;
240
241   /**
242    * The code generators used by this interceptor class generator.
243    */

244
245   public CodeGenerator[] codeGens;
246
247   /**
248    * The class transformers used by this interceptor class generator.
249    */

250
251   public ClassTransformer[] classTransformers;
252
253   /**
254    * The controller classes that can be used by the generated interceptor class.
255    */

256
257   public Class JavaDoc[] controllerClasses;
258
259   /**
260    * <tt>true</tt> if the class to be generated is an input interceptor class.
261    * An input interceptor intercepts incoming calls, in external server
262    * interfaces, while an output interceptor intercepts outgoing calls, in
263    * external client interfaces (or in the corresponding internal server
264    * interfaces).
265    */

266
267   public boolean in;
268
269   /**
270    * Indicates if the controller and interceptor classes are merged. This is
271    * equivalent to the fact that the controller class list is empty.
272    */

273
274   public boolean mergeInterceptors;
275
276   /**
277    * Indicates if the controller, interceptor and content classes are merged.
278    */

279
280   public boolean mergeAll;
281
282   /**
283    * Indicates if the interceptors are generated for a composite component. This
284    * field is only used if interceptor and controller classes are merged. It is
285    * true if the merged controller class implements ContentController.
286    */

287   
288   public boolean isComposite;
289   
290   /**
291    * Indicates if the content class must be transformed.
292    */

293
294   private boolean transformContentClass;
295
296   /**
297    * The merged controller class that is the super class of the generated class.
298    * This field is null if interceptors are not merged with controller classes.
299    */

300
301   private Class JavaDoc mergedControllerClass;
302
303   /**
304    * The content class that is the super class of the mergedController class.
305    * This field is null if not all the interceptors, controllers and content
306    * classes are merged.
307    */

308
309   private Class JavaDoc contentClass;
310
311   /**
312    * The "impl" field name.
313    */

314
315   public String JavaDoc implFieldName;
316
317   /**
318    * The "impl" field descriptor.
319    */

320
321   public String JavaDoc implFieldDesc;
322
323   /**
324    * Initializes this object with the given arguments.
325    *
326    * @param args the descriptors of the code generators to be used by this
327    * interceptor class generator. This tree must be a list of object
328    * descriptors (see @link Loader#createObject createObject}, each object
329    * descriptor describing a {@link CodeGenerator}.
330    */

331
332   public void initialize (final Tree args) {
333     codeGenDescs = args;
334   }
335
336   /**
337    * Initializes the specific fields of this class before calling the overriden
338    * method.
339    *
340    * @param name the name of the class to be generated.
341    * @param args a tree of the form "(object descriptor superClass (itf1 ...
342    * itfN) (class1 ... classM) in|out)", where itf1 ... itfN are the names
343    * of the interfaces that the generated class must implement, class1 ...
344    * classM are the names of the controller classes that can used by the
345    * generated interceptor class, and in or out indicates if an input or
346    * output interceptor class must be generated.
347    * @param loader the class loader to be used to generate the specified class.
348    * This parameter, which may be <tt>null</tt>, can be a class loader, a
349    * class, or any other object (the class loader is then found by using
350    * the <tt>getClass()</tt> and <tt>getClassLoader()</tt> methods).
351    * @param classLoader the class loader to be used to load auxiliary classes.
352    * @return a sub class of the given class that implements the given
353    * interfaces.
354    * @throws ClassGenerationException if any other problem occurs.
355    */

356
357   public byte[] generateClass (
358     final String JavaDoc name,
359     final Tree args,
360     final Loader loader,
361     final ClassLoader JavaDoc classLoader) throws ClassGenerationException
362   {
363     this.args = args;
364    
365     this.superClass = args.getSubTree(1).toString().replace('.', '/');
366     Tree controllerDescs = args.getSubTree(3);
367     controllerClasses = new Class JavaDoc[controllerDescs.getSize()];
368     for (int i = 0; i < controllerClasses.length; ++i) {
369       try {
370         controllerClasses[i] =
371           loader.loadClass(controllerDescs.getSubTree(i), classLoader);
372       } catch (Exception JavaDoc e) {
373         throw new ClassGenerationException(
374           e, args.toString(), "Cannot load one of the controller class");
375       }
376     }
377     // tests if all the controller, interceptor and content classes are merged
378
mergeInterceptors = controllerClasses.length == 0;
379     if (mergeInterceptors) {
380       try {
381         mergedControllerClass =
382           loader.loadClass(args.getSubTree(1), classLoader);
383       } catch (ClassNotFoundException JavaDoc e) {
384         throw new ClassGenerationException(
385           e,
386           args.toString(),
387           "Cannot load the '" + args.getSubTree(1) + "' class");
388       }
389       isComposite = ContentController.class.isAssignableFrom(mergedControllerClass);
390       contentClass = mergedControllerClass.getSuperclass();
391       mergeAll = !contentClass.equals(Object JavaDoc.class);
392     }
393
394     // creates the code generators
395
in = args.getSubTree(4).toString().equals("in");
396     List JavaDoc codeGenList = new ArrayList JavaDoc();
397     List JavaDoc classTransformList = new ArrayList JavaDoc();
398     for (int i = 0; i < codeGenDescs.getSize(); ++i) {
399       Object JavaDoc o;
400       try {
401         o = loader.newObject(codeGenDescs.getSubTree(i), classLoader);
402       } catch (Exception JavaDoc e) {
403         throw new ClassGenerationException(
404           e,
405           args.toString(),
406           "Cannot find the '" + codeGenDescs.getSubTree(i) +
407           "' code generator class");
408       }
409       if (o instanceof ClassTransformer) {
410         classTransformList.add(o);
411       } else if (o instanceof CodeGenerator) {
412         int type = ((CodeGenerator)o).init(this);
413         if (type == CodeGenerator.IN_OUT || (type == CodeGenerator.IN) == in) {
414           codeGenList.add(o);
415         }
416       } else {
417         throw new ClassGenerationException(
418           null,
419           args.toString(),
420           codeGenDescs.getSubTree(i)
421           + " is not a ClassTransformer or a CodeGenerator");
422       }
423     }
424     if (codeGenList.size() == 0) {
425       throw new IllegalClassDescriptorException(
426         args.toString(), "no applicable code generator");
427     }
428     codeGens = new CodeGenerator[codeGenList.size()];
429     classTransformers = new ClassTransformer[classTransformList.size()];
430     codeGenList.toArray(codeGens);
431     classTransformList.toArray(classTransformers);
432
433     if (classTransformers.length > 0) {
434       if (!mergeAll) {
435         throw new ClassGenerationException(
436           null,
437           args.toString(),
438           "Class transformers cannot be used without the "
439           + "mergeControllersInterceptorsAndContent optimization option");
440       }
441       transformContentClass = true;
442     }
443
444     return super.generateClass(name, args, loader, classLoader);
445   }
446
447   protected void parseArgs (final Tree args) {
448     super.parseArgs(args);
449     if (transformContentClass) {
450       superClass = "java/lang/Object";
451     }
452   }
453
454   /**
455    * Returns <tt>true</tt>.
456    *
457    * @return <tt>true</tt>.
458    */

459   
460   protected boolean computeMaxs () {
461     return true;
462   }
463
464   /**
465    * Returns the source of the class to be generated. This method returns
466    * "INTERCEPTOR[...]", where "..." is the name of the interface implemented by
467    * the generated class.
468    *
469    * @return the source of the class to be generated.
470    */

471
472   protected String JavaDoc getSource () {
473     String JavaDoc s = interfaces.get(0).toString();
474     if (s.lastIndexOf('/') != -1) {
475       s = s.substring(s.lastIndexOf('/') + 1);
476     }
477     return "INTERCEPTOR[" + s + "]";
478   }
479
480   /**
481    * Adds {@link Interceptor} to the list returned by the overriden
482    * method. In fact this interface is not added if the interceptors are merged
483    * with the controller objects, and if isComposite is false.
484    *
485    * @return the names of all the interfaces implemented by the generated class.
486    * @throws ClassGenerationException if a problem occurs.
487    */

488
489   protected List JavaDoc getImplementedInterfaces () throws ClassGenerationException {
490     List JavaDoc itfs = super.getImplementedInterfaces();
491     if (transformContentClass) {
492       // adds the interfaces implemented by the content
493
// and the merged controller classes
494
for (int p = 0; p < 2; ++p) {
495         Class JavaDoc[] classes;
496         if (p == 0) {
497           classes = contentClass.getInterfaces();
498         } else {
499           classes = mergedControllerClass.getInterfaces();
500         }
501         for (int i = 0; i < classes.length; i++) {
502           String JavaDoc s = Type.getInternalName(classes[i]);
503           if (!itfs.contains(s)) {
504             itfs.add(s);
505           }
506         }
507       }
508     } else if (!mergeInterceptors || isComposite) {
509       itfs.add(Type.getInternalName(Interceptor.class));
510     }
511     if (codeGens!=null) {
512         for (int cg=0; cg<codeGens.length; cg++) {
513             List JavaDoc itfsCG = codeGens[cg].getImplementedInterfaces();
514             for (int itf=0; itf<itfsCG.size(); itf++) {
515                 String JavaDoc itfName = (String JavaDoc)itfsCG.get(itf);
516                 if (!itfs.contains(itfName)) {
517                     itfs.add(itfName);
518                 }
519             }
520         }
521     }
522     return itfs;
523   }
524
525   protected void generateConstructor () throws ClassGenerationException {
526     if (transformContentClass) {
527       // do not generate any constructor:
528
// the content class constructors will be copied into the generated class
529
return;
530     }
531     super.generateConstructor();
532     CodeVisitor mv = cw.visitMethod(
533       ACC_PUBLIC, "<init>", "(Ljava/lang/Object;)V", null, null);
534     // generates the bytecode corresponding to 'super();'
535
mv.visitVarInsn(ALOAD, 0);
536     mv.visitMethodInsn(INVOKESPECIAL, superClass, "<init>", "()V");
537     // generates the bytecode corresponding to 'setFcItfDelegate(arg0);'
538
mv.visitVarInsn(ALOAD, 0);
539     mv.visitVarInsn(ALOAD, 1);
540     mv.visitMethodInsn(
541       INVOKEINTERFACE,
542       "org/objectweb/fractal/julia/Interceptor",
543       "setFcItfDelegate",
544       "(Ljava/lang/Object;)V");
545     mv.visitInsn(RETURN);
546     mv.visitMaxs(2, 2);
547   }
548
549   /**
550    * Calls the overriden method and generates the "impl" field and the methods
551    * of the {@link Interceptor Interceptor} interface. This method also
552    * generates the "initFcController" method, by concatenating the code
553    * generated by the {@link CodeGenerator#generateInitCode generateInitCode}
554    * method of each code generator. If the interceptor and controller objects
555    * are merged, and if isComposite is false, this method just calls the
556    * overriden method and sets the "impl" field to the "fcContent" field.
557    *
558    * @throws ClassGenerationException if a problem occurs.
559    */

560
561   protected void generateDefaultMethods () throws ClassGenerationException {
562     super.generateDefaultMethods();
563
564     if (mergeInterceptors && !isComposite) {
565       implFieldName = "fcContent";
566       implFieldDesc = "Ljava/lang/Object;";
567       return;
568     }
569
570     // generates the "impl" field
571

572     implFieldName = "impl";
573     if (interfaces.size() == 1) {
574       implFieldDesc = "L" + (String JavaDoc)interfaces.get(0) + ";";
575     } else {
576       implFieldDesc = "Ljava/lang/Object;";
577     }
578     cw.visitField(ACC_PRIVATE, implFieldName, implFieldDesc, null, null);
579
580     // generates the Controller method
581

582     String JavaDoc mName;
583     String JavaDoc mDesc;
584     CodeVisitor mv;
585     
586     if (!mergeInterceptors || !isComposite) {
587       mName = "initFcController";
588       mDesc = "(" + Type.getDescriptor(InitializationContext.class) + ")V";
589       mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, null, null);
590       for (int i = 0; i < codeGens.length; ++i) {
591         codeGens[i].generateInitCode(mv);
592       }
593       mv.visitInsn(RETURN);
594       mv.visitMaxs(0, 0);
595     }
596
597     // generates the Interceptor methods
598

599     mName = "getFcItfDelegate";
600     mDesc = "()Ljava/lang/Object;";
601     mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, null, null);
602     mv.visitVarInsn(ALOAD, 0);
603     mv.visitFieldInsn(GETFIELD, name, implFieldName, implFieldDesc);
604     mv.visitInsn(ARETURN);
605     mv.visitMaxs(1, 1);
606
607     mName = "setFcItfDelegate";
608     mDesc = "(Ljava/lang/Object;)V";
609     mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, null, null);
610     mv.visitVarInsn(ALOAD, 0);
611     mv.visitVarInsn(ALOAD,1);
612     if (interfaces.size() == 1) {
613       mv.visitTypeInsn(CHECKCAST, (String JavaDoc)interfaces.get(0));
614     }
615     mv.visitFieldInsn(PUTFIELD, name, implFieldName, implFieldDesc);
616     mv.visitInsn(RETURN);
617     mv.visitMaxs(2, 2);
618
619     mName = "clone";
620     mDesc = "()Ljava/lang/Object;";
621     mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, null, null);
622     if (mergeInterceptors) {
623       mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
624       mv.visitInsn(DUP);
625       mv.visitLdcInsn("Cannot use merged interceptors with collection interfaces");
626       mv.visitMethodInsn(
627         INVOKESPECIAL,
628         "java/lang/RuntimeException",
629         "<init>",
630         "(Ljava/lang/String;)V");
631       mv.visitInsn(ATHROW);
632       mv.visitMaxs(2, 2);
633     } else {
634       // cannot use super.clone(), because the clone method is not defined in all
635
// Java platforms (such as J2ME, CLDC profile)
636
mv.visitTypeInsn(NEW, name);
637       mv.visitInsn(DUP);
638       mv.visitVarInsn(ALOAD, 0);
639       mv.visitMethodInsn(
640         INVOKEINTERFACE,
641         "org/objectweb/fractal/julia/Interceptor",
642         "getFcItfDelegate",
643         "()Ljava/lang/Object;");
644       mv.visitMethodInsn(INVOKESPECIAL, name, "<init>", "(Ljava/lang/Object;)V");
645       mv.visitVarInsn(ASTORE, 1);
646       for (int i = 0; i < codeGens.length; ++i) {
647         codeGens[i].generateCloneCode(mv);
648       }
649       mv.visitVarInsn(ALOAD, 1);
650       mv.visitInsn(ARETURN);
651       mv.visitMaxs(2, 2);
652     }
653   }
654
655   protected void generateInterfaceMethods () throws ClassGenerationException {
656     if (transformContentClass) {
657       Map JavaDoc methods = new HashMap JavaDoc();
658       for (int i = 0; i < interfaces.size(); ++i) {
659         String JavaDoc s = ((String JavaDoc)interfaces.get(i)).replace('/', '.');
660         Class JavaDoc itf;
661         try {
662           itf = loader.loadClass(s, classLoader);
663         } catch (ClassNotFoundException JavaDoc e) {
664           throw new ClassGenerationException(
665             e, parameters, "Cannot load the '" + s + "' interface");
666         }
667         Method JavaDoc[] meths = itf.getMethods();
668         for (int j = 0; j < meths.length; ++j) {
669           Method JavaDoc meth = meths[j];
670           String JavaDoc desc = meth.getName() + Type.getMethodDescriptor(meth);
671           methods.put(desc, meth);
672         }
673       }
674
675       // copies the content class into the generated class
676
// (transforms it and adds interception code to it on the fly)
677
ClassVisitor cv = new FilterClassAdapter(methods, cw);
678       for (int i = classTransformers.length - 1; i >= 0; --i) {
679         classTransformers[i].setClassVisitor(cv);
680         cv = classTransformers[i];
681       }
682       try {
683         getClassReader(contentClass).accept(cv, false);
684       } catch (IOException JavaDoc e) {
685         throw new ClassGenerationException(
686           e,
687           args.toString(),
688           "Cannot read the '" + contentClass.getName() + "' class");
689       } catch (VisitException e) {
690         throw e.getException();
691       }
692
693       // renames and copies the merged controller class into the generated class
694
cv = new FilterClassAdapter(null, cw);
695       try {
696         getClassReader(mergedControllerClass).accept(cv, false);
697       } catch (IOException JavaDoc e) {
698         throw new ClassGenerationException(
699           e,
700           args.toString(),
701           "Cannot read the '" + mergedControllerClass.getName() + "' class");
702       } catch (VisitException e) {
703         throw e.getException();
704       }
705     } else {
706       super.generateInterfaceMethods();
707     }
708     for (int i = 0; i < codeGens.length; ++i) {
709       codeGens[i].close();
710     }
711   }
712
713   /**
714    * Generates the interception code for the given method.
715    *
716    * @param m the method to be generated.
717    * @throws ClassGenerationException if a problem occurs.
718    */

719
720   protected void generateMethod (final Method JavaDoc m)
721     throws ClassGenerationException
722   {
723     // generates the header of the forwarder method
724
String JavaDoc itf = Type.getInternalName(m.getDeclaringClass());
725     String JavaDoc mName = m.getName();
726     String JavaDoc mDesc = Type.getMethodDescriptor(m);
727     Class JavaDoc[] params = m.getParameterTypes();
728     Class JavaDoc result = m.getReturnType();
729     Class JavaDoc[] exceptions = m.getExceptionTypes();
730     String JavaDoc[] excepts = new String JavaDoc[exceptions.length];
731     for (int i = 0; i < exceptions.length; ++i) {
732       excepts[i] = Type.getInternalName(exceptions[i]);
733     }
734     CodeVisitor mv = cw.visitMethod(ACC_PUBLIC, mName, mDesc, excepts, null);
735
736     // creates the code adapter chain that will introduce the interception code
737
// in the original method code, i.e., in the code generated below
738
for (int i = 0; i < codeGens.length; ++i) {
739       mv = codeGens[i].generateInterceptionCode(m, mv);
740     }
741
742     // step 1 : load the "impl" field
743
mv.visitVarInsn(ALOAD, 0);
744     if (!mergeAll) {
745       mv.visitFieldInsn(GETFIELD, name, implFieldName, implFieldDesc);
746       if (interfaces.size() > 1 || !superClass.equals("java/lang/Object")) {
747         mv.visitTypeInsn(CHECKCAST, itf);
748       }
749     }
750     // step 2 : push parameters p0,...
751
int offset = 1;
752     for (int i = 0; i < params.length; ++i) {
753       mv.visitVarInsn(ILOAD + getOpcodeOffset(params[i]), offset);
754       offset += getSize(params[i]);
755     }
756     // step 3 : invoke method m
757
if (mergeAll) {
758       mv.visitMethodInsn(INVOKESPECIAL, superClass, mName, mDesc);
759     } else {
760       mv.visitMethodInsn(INVOKEINTERFACE, itf, mName, mDesc);
761     }
762     // step 4 : return the result
763
if (result == Void.TYPE) {
764       mv.visitInsn(RETURN);
765     } else {
766       mv.visitInsn(IRETURN + getOpcodeOffset(result));
767     }
768
769     // maxStack and maxLocals are automatically computed
770
mv.visitMaxs(0, 0);
771   }
772
773   /**
774    * Returns a {@link ClassReader} to analyze the given class.
775    *
776    * @param c a class.
777    * @return a {@link ClassReader} to analyze the given class.
778    * @throws IOException if the bytecode of the class cannot be found.
779    */

780
781   private ClassReader getClassReader (final Class JavaDoc c) throws IOException JavaDoc {
782     try {
783       return new ClassReader(c.getName());
784     } catch (IOException JavaDoc e) {
785       // an exception can occur if c is a dynamically generated class;
786
// indeed, in this case, it can not be found by the system class loader.
787
String JavaDoc s = Type.getInternalName(c) + ".class";
788       return new ClassReader(c.getClassLoader().getResourceAsStream(s));
789     }
790   }
791
792   /**
793    * A class adapter to rename a class, to add interception code to some
794    * methods.
795    */

796
797   private class FilterClassAdapter extends ClassAdapter {
798
799     /**
800      * The methods to which interception code must be added. This map is null
801      * for a merged controller class. For a user component class, it associates
802      * Method objects to name+desc method keys.
803      */

804
805     private Map JavaDoc methods;
806
807     /**
808      * True if the class that is being transformed is a user component class (or
809      * false if it is a merged controller class).
810      */

811
812     private boolean content;
813
814     /**
815      * Old name of the class (and of its super class if the class is a merged
816      * controller class.
817      */

818
819     private List JavaDoc oldNames;
820
821     public FilterClassAdapter (final Map JavaDoc methods, final ClassVisitor cv) {
822       super(cv);
823       this.methods = methods;
824       if (methods != null) {
825         content = true;
826         oldNames = Arrays.asList(new String JavaDoc[] {
827           Type.getInternalName(contentClass)
828         });
829       } else {
830         content = false;
831         oldNames = Arrays.asList(new String JavaDoc[] {
832           Type.getInternalName(contentClass),
833           Type.getInternalName(mergedControllerClass)
834         });
835       }
836     }
837
838     public void visit (
839       final int version,
840       final int access,
841       final String JavaDoc name,
842       final String JavaDoc superName,
843       final String JavaDoc[] interfaces,
844       final String JavaDoc sourceFile)
845     {
846       // does nothing
847
}
848
849     public CodeVisitor visitMethod (
850       final int access,
851       final String JavaDoc name,
852       final String JavaDoc desc,
853       final String JavaDoc[] exceptions,
854       final Attribute attrs)
855     {
856       int newAccess = access;
857       String JavaDoc newName = name;
858       if (content) {
859         if (FRACTAL_METHODS.contains(name)) {
860           newAccess = ACC_PRIVATE;
861           newName = "super$" + name;
862         }
863       } else {
864         if (name.equals("<init>") || name.equals("getFcGeneratorParameters")) {
865           return null;
866         }
867       }
868       CodeVisitor v = cv.visitMethod(newAccess, newName, desc, exceptions, attrs);
869       if (content) {
870         Method JavaDoc m = (Method JavaDoc)methods.get(name + desc);
871         if (m != null) {
872           for (int i = 0; i < codeGens.length; ++i) {
873             try {
874               v = codeGens[i].generateInterceptionCode(m, v);
875             } catch (ClassGenerationException e) {
876               throw new VisitException(e);
877             }
878           }
879         }
880       }
881       return new FilterCodeAdapter(oldNames, v);
882     }
883
884     public void visitEnd () {
885       // does nothing
886
}
887   }
888
889
890   /**
891    * A code adapter to update class name references in a method.
892    */

893
894   private class FilterCodeAdapter extends CodeAdapter {
895
896     /**
897      * Old name of the class (and, optionally, of its super class).
898      */

899
900     private List JavaDoc oldNames;
901
902     public FilterCodeAdapter (final List JavaDoc oldNames, final CodeVisitor cv) {
903       super(cv);
904       this.oldNames = oldNames;
905     }
906
907     public void visitFieldInsn (
908       final int opcode,
909       final String JavaDoc owner,
910       final String JavaDoc name,
911       final String JavaDoc desc)
912     {
913       String JavaDoc newOwner = owner;
914       if (oldNames.contains(owner)) {
915         newOwner = InterceptorClassGenerator.this.name;
916       }
917       cv.visitFieldInsn(opcode, newOwner, name, desc);
918     }
919
920     public void visitMethodInsn (
921       final int opcode,
922       final String JavaDoc owner,
923       final String JavaDoc name,
924       final String JavaDoc desc)
925     {
926       String JavaDoc newOwner = owner;
927       String JavaDoc newName = name;
928       if (oldNames.contains(owner)) {
929         newOwner = InterceptorClassGenerator.this.name;
930       }
931       if (oldNames.size() == 2 && owner.equals(oldNames.get(0))) {
932         // call from merged controller class to content class
933
if (FRACTAL_METHODS.contains(name)) {
934           newName = "super$" + name;
935         }
936       }
937       cv.visitMethodInsn(opcode, newOwner, newName, desc);
938     }
939   }
940 }
941
Popular Tags