KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > trove > util > MergedClass


1 /* ====================================================================
2  * Trove - Copyright (c) 1997-2000 Walt Disney Internet Group
3  * ====================================================================
4  * The Tea Software License, Version 1.1
5  *
6  * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Walt Disney Internet Group (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@dig.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35  * written permission of the Walt Disney Internet Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.trove.util;
54
55 import java.lang.reflect.*;
56 import java.util.*;
57 import java.io.OutputStream JavaDoc;
58 import java.io.IOException JavaDoc;
59 import com.go.trove.classfile.*;
60
61 /******************************************************************************
62  * Merges several classes together, producing a new class that has all of the
63  * methods of the combined classes. All methods in the combined class delegate
64  * to instances of the source classes. If multiple classes implement the same
65  * method, the first one provided is used. The merged class implements all of
66  * the interfaces provided by the source classes or interfaces.
67  *
68  * <p>This class performs a function almost the same as the Proxy class
69  * introduced in JDK1.3. It differs in that it supports classes as well as
70  * interfaces as input, and it binds to wrapped objects without using
71  * runtime reflection. It is less flexible than Proxy in that there isn't way
72  * to customize the method delegation, and so it isn't suitable for creating
73  * dynamically generated interface implementations.
74  *
75  * @author Brian S O'Neill
76  * @version
77  * <!--$$Revision:--> 30 <!-- $-->, <!--$$JustDate:--> 01/02/20 <!-- $-->
78  */

79 public class MergedClass {
80     // Maps ClassInjectors to Maps that map to merged class names.
81
// Map<ClassInjector, Map<MultiKey, String>>
82
// The MultiKey is composed of class names and method prefixes. By storing
83
// class names into the maps instead of classes, the classes may be
84
// reclaimed by the garbage collector.
85
private static Map cMergedMap;
86
87     static {
88         try {
89             cMergedMap = new IdentityMap(7);
90         }
91         catch (LinkageError JavaDoc e) {
92             cMergedMap = new HashMap(7);
93         }
94         catch (Exception JavaDoc e) {
95             // Microsoft VM sometimes throws an undeclared
96
// ClassNotFoundException instead of doing the right thing and
97
// throwing some form of a LinkageError if the class couldn't
98
// be found.
99
cMergedMap = new HashMap(7);
100         }
101     }
102
103     /**
104      * Returns the constructor for a class that merges all of the given source
105      * classes. The constructor's parameter types match the source classes.
106      * An IllegalArgumentException is thrown if any of the given conditions
107      * are not satisfied:
108      *
109      * <ul>
110      * <li>None of the given classes can represent a primitive type
111      * <li>A source class can only be provided once
112      * <li>Any non-public classes must be in the same common package
113      * <li>Duplicate methods cannot have conflicting return types
114      * <li>The given classes must be loadable from the given injector
115      * <li>At most 254 classes may be merged
116      * </ul>
117      *
118      * Note: Because a constructor is limited to 254 parameters, if more than
119      * 254 classes are to be merged together, call {@link #getConstructor2}.
120      *
121      * @param injector ClassInjector that will receive class definition
122      * @param classes Source classes used to derive merged class
123      */

124     public static Constructor getConstructor(ClassInjector injector,
125                                              Class JavaDoc[] classes)
126         throws IllegalArgumentException JavaDoc
127     {
128         return getConstructor(injector, classes, null);
129     }
130
131     /**
132      * Returns the constructor for a class that merges all of the given source
133      * classes. The constructor's parameter types match the source classes.
134      * An IllegalArgumentException is thrown if any of the given conditions
135      * are not satisfied:
136      *
137      * <ul>
138      * <li>None of the given classes can represent a primitive type
139      * <li>A source class can only be provided once, unless paired with a
140      * unique prefix.
141      * <li>Any non-public classes must be in the same common package
142      * <li>Duplicate methods cannot have conflicting return types, unless a
143      * prefix is provided.
144      * <li>The given classes must be loadable from the given injector
145      * <li>At most 254 classes may be merged
146      * </ul>
147      *
148      * To help resolve ambiguities, a method prefix can be specified for each
149      * passed in class. For each prefixed method, the non-prefixed method is
150      * also generated, unless that method conflicts with the return type of
151      * another method. If any passed in classes are or have interfaces, then
152      * those interfaces will be implemented only if there are no conflicts.
153      *
154      * <p>The array of prefixes may be null, have null elements or
155      * be shorter than the array of classes. A prefix of "" is treated as null.
156      * <p>
157      * Note: Because a constructor is limited to 254 parameters, if more than
158      * 254 classes are to be merged together, call {@link #getConstructor2}.
159      *
160      * @param injector ClassInjector that will receive class definition
161      * @param classes Source classes used to derive merged class
162      * @param prefixes Optional prefixes to apply to methods of each generated
163      * class to eliminate duplicate method names
164      */

165     public static Constructor getConstructor(ClassInjector injector,
166                                              Class JavaDoc[] classes,
167                                              String JavaDoc[] prefixes)
168         throws IllegalArgumentException JavaDoc
169     {
170         if (classes.length > 254) {
171             throw new IllegalArgumentException JavaDoc
172                 ("More than 254 merged classes: " + classes.length);
173         }
174
175         Class JavaDoc clazz = getMergedClass(injector, classes, prefixes);
176
177         try {
178             return clazz.getConstructor(classes);
179         }
180         catch (NoSuchMethodException JavaDoc e) {
181             throw new InternalError JavaDoc(e.toString());
182         }
183     }
184
185     /**
186      * Returns the constructor for a class that merges all of the given source
187      * classes. The constructor accepts one parameter type: an
188      * {@link InstanceFactory}. Merged instances are requested only when
189      * first needed. An IllegalArgumentException is thrown if any of the given
190      * conditions are not satisfied:
191      *
192      * <ul>
193      * <li>None of the given classes can represent a primitive type
194      * <li>A source class can only be provided once
195      * <li>Any non-public classes must be in the same common package
196      * <li>Duplicate methods cannot have conflicting return types
197      * <li>The given classes must be loadable from the given injector
198      * </ul>
199      *
200      * @param injector ClassInjector that will receive class definition
201      * @param classes Source classes used to derive merged class
202      */

203     public static Constructor getConstructor2(ClassInjector injector,
204                                               Class JavaDoc[] classes)
205         throws IllegalArgumentException JavaDoc
206     {
207         return getConstructor2(injector, classes, null);
208     }
209
210     /**
211      * Returns the constructor for a class that merges all of the given source
212      * classes. The constructor accepts one parameter type: an
213      * {@link InstanceFactory}. Merged instances are requested only when
214      * first needed. An IllegalArgumentException is thrown if any of the given
215      * conditions are not satisfied:
216      *
217      * <ul>
218      * <li>None of the given classes can represent a primitive type
219      * <li>A source class can only be provided once, unless paired with a
220      * unique prefix.
221      * <li>Any non-public classes must be in the same common package
222      * <li>Duplicate methods cannot have conflicting return types, unless a
223      * prefix is provided.
224      * <li>The given classes must be loadable from the given injector
225      * </ul>
226      *
227      * To help resolve ambiguities, a method prefix can be specified for each
228      * passed in class. For each prefixed method, the non-prefixed method is
229      * also generated, unless that method conflicts with the return type of
230      * another method. If any passed in classes are or have interfaces, then
231      * those interfaces will be implemented only if there are no conflicts.
232      *
233      * <p>The array of prefixes may be null, have null elements or
234      * be shorter than the array of classes. A prefix of "" is treated as null.
235      *
236      * @param injector ClassInjector that will receive class definition
237      * @param classes Source classes used to derive merged class
238      * @param prefixes Optional prefixes to apply to methods of each generated
239      * class to eliminate duplicate method names
240      */

241     public static Constructor getConstructor2(ClassInjector injector,
242                                               Class JavaDoc[] classes,
243                                               String JavaDoc[] prefixes)
244         throws IllegalArgumentException JavaDoc
245     {
246         Class JavaDoc clazz = getMergedClass(injector, classes, prefixes);
247
248         try {
249             return clazz.getConstructor(new Class JavaDoc[]{InstanceFactory.class});
250         }
251         catch (NoSuchMethodException JavaDoc e) {
252             throw new InternalError JavaDoc(e.toString());
253         }
254     }
255
256     private static Class JavaDoc getMergedClass(ClassInjector injector,
257                                         Class JavaDoc[] classes,
258                                         String JavaDoc[] prefixes)
259         throws IllegalArgumentException JavaDoc
260     {
261         ClassEntry[] classEntries = new ClassEntry[classes.length];
262         for (int i=0; i<classes.length; i++) {
263             // Load the classes from the ClassInjector, just like they will be
264
// when the generated class is resolved.
265
try {
266                 classes[i] = injector.loadClass(classes[i].getName());
267             }
268             catch (ClassNotFoundException JavaDoc e) {
269                 throw new IllegalArgumentException JavaDoc
270                     ("Unable to load class from injector: " + classes[i]);
271             }
272
273             if (prefixes == null || i >= prefixes.length) {
274                 classEntries[i] = new ClassEntry(classes[i]);
275             }
276             else {
277                 String JavaDoc prefix = prefixes[i];
278                 if (prefix != null && prefix.length() == 0) {
279                     prefix = null;
280                 }
281                 classEntries[i] = new ClassEntry(classes[i], prefix);
282             }
283         }
284
285         return getMergedClass(injector, classEntries);
286     }
287
288     /**
289      * Creates a class with a public constructor whose parameter types match
290      * the given source classes. Another constructor is also created that
291      * accepts an InstanceFactory.
292      */

293     private static synchronized Class JavaDoc getMergedClass(ClassInjector injector,
294                                                      ClassEntry[] classEntries)
295         throws IllegalArgumentException JavaDoc
296     {
297         Map classListMap = (Map)cMergedMap.get(injector);
298         if (classListMap == null) {
299             classListMap = new HashMap(7);
300             cMergedMap.put(injector, classListMap);
301         }
302
303         Object JavaDoc key = generateKey(classEntries);
304         String JavaDoc mergedName = (String JavaDoc)classListMap.get(key);
305         if (mergedName != null) {
306             try {
307                 return injector.loadClass(mergedName);
308             }
309             catch (ClassNotFoundException JavaDoc e) {
310             }
311         }
312
313         ClassFile cf;
314         try {
315             cf = createClassFile(injector, classEntries);
316         }
317         catch (IllegalArgumentException JavaDoc e) {
318             e.fillInStackTrace();
319             throw e;
320         }
321
322         /*
323         try {
324             java.io.FileOutputStream out =
325                 new java.io.FileOutputStream(cf.getClassName() + ".class");
326             cf.writeTo(out);
327             out.close();
328         }
329         catch (Exception e) {
330             e.printStackTrace();
331         }
332         */

333
334         try {
335             OutputStream JavaDoc stream = injector.getStream(cf.getClassName());
336             cf.writeTo(stream);
337             stream.close();
338         }
339         catch (IOException JavaDoc e) {
340             throw new InternalError JavaDoc(e.toString());
341         }
342
343         Class JavaDoc merged;
344         try {
345             merged = injector.loadClass(cf.getClassName());
346         }
347         catch (ClassNotFoundException JavaDoc e) {
348             throw new InternalError JavaDoc(e.toString());
349         }
350
351         classListMap.put(key, merged.getName());
352         return merged;
353     }
354
355     private static Object JavaDoc generateKey(ClassEntry[] classEntries) {
356         int length = classEntries.length;
357         Object JavaDoc[] mainElements = new Object JavaDoc[length];
358         for (int i=0; i<length; i++) {
359             ClassEntry classEntry = classEntries[i];
360             mainElements[i] = new MultiKey(new Object JavaDoc[] {
361                 classEntry.getClazz().getName(),
362                 classEntry.getMethodPrefix()
363             });
364         }
365         return new MultiKey(mainElements);
366     }
367
368     private static ClassFile createClassFile(ClassInjector injector,
369                                              ClassEntry[] classEntries)
370         throws IllegalArgumentException JavaDoc
371     {
372         Set classSet = new HashSet(classEntries.length * 2 + 1);
373         Set nonConflictingClasses = new HashSet(classEntries.length * 2 + 1);
374         String JavaDoc commonPackage = null;
375         Map methodMap = new HashMap();
376
377         for (int i=0; i<classEntries.length; i++) {
378             ClassEntry classEntry = classEntries[i];
379             Class JavaDoc clazz = classEntry.getClazz();
380             
381             if (clazz.isPrimitive()) {
382                 throw new IllegalArgumentException JavaDoc
383                     ("Merged classes cannot be primitive: " + clazz);
384             }
385
386             if (!classSet.add(classEntry)) {
387                 throw new IllegalArgumentException JavaDoc
388                     ("Class is specified more than once: " + clazz);
389             }
390
391             if (!Modifier.isPublic(clazz.getModifiers())) {
392                 String JavaDoc classPackage = clazz.getName();
393                 int index = classPackage.lastIndexOf('.');
394                 if (index < 0) {
395                     classPackage = "";
396                 }
397                 else {
398                     classPackage = classPackage.substring(0, index);
399                 }
400
401                 if (commonPackage == null) {
402                     commonPackage = classPackage;
403                 }
404                 else if (!commonPackage.equals(classPackage)) {
405                     throw new IllegalArgumentException JavaDoc
406                         ("Not all non-public classes defined in same " +
407                          "package: " + commonPackage);
408                 }
409             }
410
411             // Innocent until proven guilty.
412
nonConflictingClasses.add(classEntry);
413
414             Method[] methods = clazz.getMethods();
415             String JavaDoc prefix = classEntry.getMethodPrefix();
416
417             for (int j=0; j<methods.length; j++) {
418                 Method method = methods[j];
419                 String JavaDoc name = method.getName();
420                 // Workaround for JDK1.2 bug #4187388.
421
if ("<clinit>".equals(name)) {
422                     continue;
423                 }
424
425                 MethodEntry methodEntry = new MethodEntry(method, name);
426                 MethodEntry existing = (MethodEntry)methodMap.get(methodEntry);
427
428                 if (existing == null) {
429                     methodMap.put(methodEntry, methodEntry);
430                 }
431                 else if (existing.returnTypeDiffers(methodEntry)) {
432                     nonConflictingClasses.remove(classEntry);
433                     if (prefix == null) {
434                         throw new IllegalArgumentException JavaDoc
435                             ("Conflicting return types: " +
436                              existing + ", " + methodEntry);
437                     }
438                 }
439
440                 if (prefix != null) {
441                     name = prefix + name;
442                 
443                     methodEntry = new MethodEntry(method, name);
444                     existing = (MethodEntry)methodMap.get(methodEntry);
445
446                     if (existing == null) {
447                         methodMap.put(methodEntry, methodEntry);
448                     }
449                     else if (existing.returnTypeDiffers(methodEntry)) {
450                         nonConflictingClasses.remove(classEntry);
451                         throw new IllegalArgumentException JavaDoc
452                             ("Conflicting return types: " +
453                              existing + ", " + methodEntry);
454                     }
455                 }
456             }
457         }
458
459         int id = 0;
460         Iterator it = classSet.iterator();
461         while (it.hasNext()) {
462             id = id * 31 + it.next().hashCode();
463         }
464
465         String JavaDoc className = "MergedClass$";
466         try {
467             while (true) {
468                 className = "MergedClass$" + (id & 0xffffffffL);
469                 if (commonPackage != null && commonPackage.length() > 0) {
470                     className = commonPackage + '.' + className;
471                 }
472                 try {
473                     injector.loadClass(className);
474                 }
475                 catch (LinkageError JavaDoc e) {
476                 }
477                 id++;
478             }
479         }
480         catch (ClassNotFoundException JavaDoc e) {
481         }
482
483         ClassFile cf = new ClassFile(className);
484         cf.getAccessFlags().setFinal(true);
485         cf.markSynthetic();
486
487         for (int i=0; i<classEntries.length; i++) {
488             ClassEntry classEntry = classEntries[i];
489             if (nonConflictingClasses.contains(classEntry)) {
490                 addAllInterfaces(cf, classEntry.getClazz());
491             }
492         }
493
494         AccessFlags privateAccess = new AccessFlags();
495         privateAccess.setPrivate(true);
496         AccessFlags privateFinalAccess = new AccessFlags();
497         privateFinalAccess.setPrivate(true);
498         privateFinalAccess.setFinal(true);
499         AccessFlags publicAccess = new AccessFlags();
500         publicAccess.setPublic(true);
501
502         // Add field to store optional InstanceFactory.
503
TypeDescriptor instanceFactoryType =
504             new TypeDescriptor(InstanceFactory.class);
505         cf.addField(privateAccess,
506                     "instanceFactory", instanceFactoryType).markSynthetic();
507
508         Method instanceFactoryMethod;
509         try {
510             instanceFactoryMethod =
511                 InstanceFactory.class.getMethod
512                 ("getInstance", new Class JavaDoc[]{int.class});
513         }
514         catch (NoSuchMethodException JavaDoc e) {
515             throw new InternalError JavaDoc(e.toString());
516         }
517
518         // Define fields which point to wrapped objects, and define methods
519
// to access the fields.
520
String JavaDoc[] fieldNames = new String JavaDoc[classEntries.length];
521         TypeDescriptor[] types = new TypeDescriptor[classEntries.length];
522         for (int i=0; i<classEntries.length; i++) {
523             Class JavaDoc clazz = classEntries[i].getClazz();
524             String JavaDoc fieldName = "m$" + i;
525             TypeDescriptor type = new TypeDescriptor(clazz);
526             cf.addField(privateAccess, fieldName, type).markSynthetic();
527             fieldNames[i] = fieldName;
528             types[i] = type;
529             
530             // Create method that returns field, calling the InstanceFactory
531
// if necessary to initialize for the first time.
532
MethodInfo mi = cf.addMethod
533                 (privateFinalAccess, fieldName, type, null);
534             mi.markSynthetic();
535             CodeBuilder builder = new CodeBuilder(mi);
536             builder.loadThis();
537             builder.loadField(fieldName, type);
538             builder.dup();
539             Label isNull = builder.createLabel();
540             builder.ifNullBranch(isNull, true);
541             // Return the initialized field.
542
builder.returnValue(Object JavaDoc.class);
543             isNull.setLocation();
544             // Discard null field value.
545
builder.pop();
546             builder.loadThis();
547             builder.loadField("instanceFactory", instanceFactoryType);
548             builder.dup();
549             Label haveInstanceFactory = builder.createLabel();
550             builder.ifNullBranch(haveInstanceFactory, false);
551             // No instanceFactory: return null.
552
builder.loadConstant(null);
553             builder.returnValue(Object JavaDoc.class);
554             haveInstanceFactory.setLocation();
555             builder.loadConstant(i);
556             builder.invoke(instanceFactoryMethod);
557             builder.checkCast(type);
558             builder.dup();
559             builder.loadThis();
560             builder.swap();
561             builder.storeField(fieldName, type);
562             builder.returnValue(Object JavaDoc.class);
563         }
564
565         // Define a constructor that initializes fields from an Object array.
566
if (classEntries.length <= 254) {
567             MethodInfo mi = cf.addConstructor(publicAccess, types);
568             
569             CodeBuilder builder = new CodeBuilder(mi);
570             builder.loadThis();
571             builder.invokeSuperConstructor(null);
572             LocalVariable[] params = builder.getParameters();
573             
574             for (int i=0; i<classEntries.length; i++) {
575                 builder.loadThis();
576                 builder.loadLocal(params[i]);
577                 builder.storeField(fieldNames[i], types[i]);
578             }
579             
580             builder.returnVoid();
581             builder = null;
582         }
583
584         // Define a constructor that saves an InstanceFactory.
585
MethodInfo mi = cf.addConstructor
586             (publicAccess, new TypeDescriptor[]{instanceFactoryType});
587
588         CodeBuilder builder = new CodeBuilder(mi);
589         builder.loadThis();
590         builder.invokeSuperConstructor(null);
591         builder.loadThis();
592         builder.loadLocal(builder.getParameters()[0]);
593         builder.storeField("instanceFactory", instanceFactoryType);
594
595         builder.returnVoid();
596         builder = null;
597
598         Set methodSet = methodMap.keySet();
599
600         // Define all the wrapper methods.
601
for (int i=0; i<classEntries.length; i++) {
602             ClassEntry classEntry = classEntries[i];
603             String JavaDoc prefix = classEntry.getMethodPrefix();
604             String JavaDoc fieldName = fieldNames[i];
605             TypeDescriptor type = types[i];
606
607             Method[] methods = classEntry.getClazz().getMethods();
608             for (int j=0; j<methods.length; j++) {
609                 Method method = methods[j];
610                 // Workaround for JDK1.2 bug #4187388.
611
if ("<clinit>".equals(method.getName())) {
612                     continue;
613                 }
614
615                 MethodEntry methodEntry = new MethodEntry(method);
616                 if (methodSet.contains(methodEntry)) {
617                     methodSet.remove(methodEntry);
618                     addWrapperMethod(cf, methodEntry, fieldName, type);
619                 }
620
621                 if (prefix != null) {
622                     methodEntry = new MethodEntry
623                         (method, prefix + method.getName());
624                     if (methodSet.contains(methodEntry)) {
625                         methodSet.remove(methodEntry);
626                         addWrapperMethod(cf, methodEntry, fieldName, type);
627                     }
628                 }
629             }
630         }
631
632         return cf;
633     }
634
635     private static void addAllInterfaces(ClassFile cf, Class JavaDoc clazz) {
636         if (clazz == null) {
637             return;
638         }
639
640         if (clazz.isInterface()) {
641             cf.addInterface(clazz);
642         }
643
644         addAllInterfaces(cf, clazz.getSuperclass());
645
646         Class JavaDoc[] interfaces = clazz.getInterfaces();
647         for (int i=0; i<interfaces.length; i++) {
648             addAllInterfaces(cf, interfaces[i]);
649         }
650     }
651
652     private static void addWrapperMethod(ClassFile cf,
653                                          MethodEntry methodEntry,
654                                          String JavaDoc fieldName,
655                                          TypeDescriptor type) {
656         Method method = methodEntry.getMethod();
657
658         // Don't override any methods in Object, especially final ones.
659
if (isDefinedInObject(method)) {
660             return;
661         }
662
663         AccessFlags flags = new AccessFlags(method.getModifiers());
664         flags.setAbstract(false);
665         flags.setFinal(true);
666         flags.setSynchronized(false);
667         flags.setNative(false);
668         flags.setStatic(false);
669
670         AccessFlags staticFlags = (AccessFlags)flags.clone();
671         staticFlags.setStatic(true);
672
673         TypeDescriptor ret = new TypeDescriptor(method.getReturnType());
674
675         Class JavaDoc[] paramClasses = method.getParameterTypes();
676         TypeDescriptor[] params = new TypeDescriptor[paramClasses.length];
677         for (int i=0; i<params.length; i++) {
678             params[i] = new TypeDescriptor(paramClasses[i]);
679         }
680
681         MethodInfo mi;
682         if (Modifier.isStatic(method.getModifiers())) {
683             mi = cf.addMethod
684                 (staticFlags, methodEntry.getName(), ret, params);
685         }
686         else {
687             mi = cf.addMethod(flags, methodEntry.getName(), ret, params);
688         }
689         
690         // Exception stuff...
691
Class JavaDoc[] exceptions = method.getExceptionTypes();
692         for (int i=0; i<exceptions.length; i++) {
693             mi.addException(exceptions[i].getName());
694         }
695
696         // Delegate to wrapped object.
697
CodeBuilder builder = new CodeBuilder(mi);
698
699         if (!Modifier.isStatic(method.getModifiers())) {
700             builder.loadThis();
701             builder.loadField(fieldName, type);
702             Label isNonNull = builder.createLabel();
703             builder.dup();
704             builder.ifNullBranch(isNonNull, false);
705             // Discard null field value.
706
builder.pop();
707             // Call the field access method, which in turn calls the
708
// InstanceFactory.
709
builder.loadThis();
710             builder.invokePrivate(fieldName, type, null);
711             isNonNull.setLocation();
712         }
713         LocalVariable[] locals = builder.getParameters();
714         for (int i=0; i<locals.length; i++) {
715             builder.loadLocal(locals[i]);
716         }
717         builder.invoke(method);
718
719         if (method.getReturnType() == void.class) {
720             builder.returnVoid();
721         }
722         else {
723             builder.returnValue(method.getReturnType());
724         }
725     }
726
727     private static boolean isDefinedInObject(Method method) {
728         if (method.getDeclaringClass() == Object JavaDoc.class) {
729             return true;
730         }
731
732         Class JavaDoc[] types = method.getParameterTypes();
733         String JavaDoc name = method.getName();
734         
735         if (types.length == 0) {
736             return
737                 "hashCode".equals(name) ||
738                 "clone".equals(name) ||
739                 "toString".equals(name) ||
740                 "finalize".equals(name);
741         }
742         else {
743             return
744                 types.length == 1 &&
745                 types[0] == Object JavaDoc.class &&
746                 "equals".equals(name);
747         }
748     }
749
750     private MergedClass() {
751     }
752
753     /**
754      * InstanceFactory allows merged class instances to be requested only
755      * when first needed.
756      */

757     public interface InstanceFactory {
758         /**
759          * Return a merged class instance by index. This index corresponds to
760          * the class index used when defining the MergedClass.
761          */

762         public Object JavaDoc getInstance(int index);
763     }
764
765     private static class ClassEntry {
766         private final Class JavaDoc mClazz;
767         private final String JavaDoc mPrefix;
768
769         public ClassEntry(Class JavaDoc clazz) {
770             this(clazz, null);
771         }
772         
773         public ClassEntry(Class JavaDoc clazz, String JavaDoc prefix) {
774             mClazz = clazz;
775             mPrefix = prefix;
776         }
777
778         public Class JavaDoc getClazz() {
779             return mClazz;
780         }
781
782         public String JavaDoc getMethodPrefix() {
783             return mPrefix;
784         }
785
786         public int hashCode() {
787             int hash = mClazz.getName().hashCode();
788             return (mPrefix == null) ? hash : hash ^ mPrefix.hashCode();
789         }
790
791         public boolean equals(Object JavaDoc other) {
792             if (other instanceof ClassEntry) {
793                 ClassEntry classEntry = (ClassEntry)other;
794                 if (mClazz == classEntry.mClazz) {
795                     if (mPrefix == null) {
796                         return classEntry.mPrefix == null;
797                     }
798                     else {
799                         return mPrefix.equals(classEntry.mPrefix);
800                     }
801                 }
802             }
803             return false;
804         }
805
806         public String JavaDoc toString() {
807             return mClazz.toString();
808         }
809     }
810
811     private static class MethodEntry {
812         private final Method mMethod;
813         private final String JavaDoc mName;
814         private List mParams;
815         private int mHashCode;
816
817         public MethodEntry(Method method) {
818             this(method, method.getName());
819         }
820
821         public MethodEntry(Method method, String JavaDoc name) {
822             mMethod = method;
823             mName = name;
824             mParams = Arrays.asList(method.getParameterTypes());
825             mHashCode = mName.hashCode() ^ mParams.hashCode();
826         }
827
828         public Method getMethod() {
829             return mMethod;
830         }
831
832         public String JavaDoc getName() {
833             return mName;
834         }
835
836         public boolean returnTypeDiffers(MethodEntry methodEntry) {
837             return getMethod().getReturnType() !=
838                 methodEntry.getMethod().getReturnType();
839         }
840
841         public int hashCode() {
842             return mHashCode;
843         }
844
845         public boolean equals(Object JavaDoc other) {
846             if (!(other instanceof MethodEntry)) {
847                 return false;
848             }
849             MethodEntry methodEntry = (MethodEntry)other;
850             return mName.equals(methodEntry.mName) &&
851                 mParams.equals(methodEntry.mParams);
852         }
853
854         public String JavaDoc toString() {
855             return mMethod.toString();
856         }
857     }
858 }
859
Popular Tags