001: /***** BEGIN LICENSE BLOCK *****
002: * Version: CPL 1.0/GPL 2.0/LGPL 2.1
003: *
004: * The contents of this file are subject to the Common Public
005: * License Version 1.0 (the "License"); you may not use this file
006: * except in compliance with the License. You may obtain a copy of
007: * the License at http://www.eclipse.org/legal/cpl-v10.html
008: *
009: * Software distributed under the License is distributed on an "AS
010: * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
011: * implied. See the License for the specific language governing
012: * rights and limitations under the License.
013: *
014: * Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
015: *
016: * Alternatively, the contents of this file may be used under the terms of
017: * either of the GNU General Public License Version 2 or later (the "GPL"),
018: * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
019: * in which case the provisions of the GPL or the LGPL are applicable instead
020: * of those above. If you wish to allow use of your version of this file only
021: * under the terms of either the GPL or the LGPL, and not to allow others to
022: * use your version of this file under the terms of the CPL, indicate your
023: * decision by deleting the provisions above and replace them with the notice
024: * and other provisions required by the GPL or the LGPL. If you do not delete
025: * the provisions above, a recipient may use your version of this file under
026: * the terms of any one of the CPL, the GPL or the LGPL.
027: ***** END LICENSE BLOCK *****/package org.jruby.javasupport.proxy;
028:
029: import java.lang.reflect.Constructor;
030: import java.lang.reflect.Field;
031: import java.lang.reflect.InvocationTargetException;
032: import java.lang.reflect.Method;
033: import java.lang.reflect.Modifier;
034: import java.lang.reflect.UndeclaredThrowableException;
035: import java.security.AccessController;
036: import java.security.PrivilegedAction;
037: import java.util.Arrays;
038: import java.util.Collections;
039: import java.util.HashMap;
040: import java.util.HashSet;
041: import java.util.Iterator;
042: import java.util.Map;
043: import java.util.Set;
044:
045: import org.objectweb.asm.ClassVisitor;
046: import org.objectweb.asm.ClassWriter;
047: import org.objectweb.asm.FieldVisitor;
048: import org.objectweb.asm.Label;
049: import org.objectweb.asm.Opcodes;
050: import org.objectweb.asm.Type;
051: import org.objectweb.asm.commons.GeneratorAdapter;
052:
053: public class JavaProxyClassFactory {
054:
055: private static final Type JAVA_LANG_CLASS_TYPE = Type
056: .getType(Class.class);
057:
058: private static final Type[] EMPTY_TYPE_ARR = new Type[0];
059:
060: private static final org.objectweb.asm.commons.Method HELPER_GET_PROXY_CLASS_METHOD = org.objectweb.asm.commons.Method
061: .getMethod(JavaProxyClass.class.getName()
062: + " initProxyClass(java.lang.Class)");
063:
064: private static final org.objectweb.asm.commons.Method CLASS_FORNAME_METHOD = org.objectweb.asm.commons.Method
065: .getMethod("java.lang.Class forName(java.lang.String)");
066:
067: private static final String INVOCATION_HANDLER_FIELD_NAME = "__handler";
068:
069: private static final String PROXY_CLASS_FIELD_NAME = "__proxy_class";
070:
071: private static final Class[] EMPTY_CLASS_ARR = new Class[0];
072:
073: private static final Type INVOCATION_HANDLER_TYPE = Type
074: .getType(JavaProxyInvocationHandler.class);
075:
076: private static final Type PROXY_METHOD_TYPE = Type
077: .getType(JavaProxyMethod.class);
078:
079: private static final Type PROXY_CLASS_TYPE = Type
080: .getType(JavaProxyClass.class);
081:
082: private static final org.objectweb.asm.commons.Method INVOCATION_HANDLER_INVOKE_METHOD = org.objectweb.asm.commons.Method
083: .getMethod("java.lang.Object invoke(java.lang.Object, "
084: + PROXY_METHOD_TYPE.getClassName()
085: + ", java.lang.Object[])");
086:
087: private static final Type PROXY_HELPER_TYPE = Type
088: .getType(InternalJavaProxyHelper.class);
089:
090: private static final org.objectweb.asm.commons.Method PROXY_HELPER_GET_METHOD = org.objectweb.asm.commons.Method
091: .getMethod(PROXY_METHOD_TYPE.getClassName()
092: + " initProxyMethod("
093: + JavaProxyClass.class.getName()
094: + ",java.lang.String,java.lang.String,boolean)");
095:
096: private static final Type JAVA_PROXY_TYPE = Type
097: .getType(InternalJavaProxy.class);
098:
099: private static int counter;
100:
101: private static Map proxies = Collections
102: .synchronizedMap(new HashMap());
103:
104: private static Method defineClass_method; // statically initialized below
105:
106: private static synchronized int nextId() {
107: return counter++;
108: }
109:
110: // TODO: we should be able to optimize this quite a bit post-1.0. JavaClass already
111: // has all the methods organized by method name; the next version (supporting protected
112: // methods/fields) will have them organized even further. So collectMethods here can
113: // just lookup the overridden methods in the JavaClass map, should be much faster.
114: static JavaProxyClass newProxyClass(ClassLoader loader,
115: String targetClassName, Class super Class,
116: Class[] interfaces, Set names)
117: throws InvocationTargetException {
118: if (loader == null) {
119: loader = JavaProxyClassFactory.class.getClassLoader();
120: }
121:
122: if (super Class == null) {
123: super Class = Object.class;
124: }
125:
126: if (interfaces == null) {
127: interfaces = EMPTY_CLASS_ARR;
128: }
129:
130: Set key = new HashSet();
131: key.add(super Class);
132: for (int i = 0; i < interfaces.length; i++) {
133: key.add(interfaces[i]);
134: }
135:
136: // add (potentially) overridden names to the key.
137: // TODO: see note above re: optimizations
138: if (names != null) {
139: key.addAll(names);
140: }
141:
142: JavaProxyClass proxyClass = (JavaProxyClass) proxies.get(key);
143: if (proxyClass == null) {
144:
145: if (targetClassName == null) {
146: String pkg = packageName(super Class);
147: String fullName = super Class.getName();
148: int ix = fullName.lastIndexOf('.');
149: String cName = fullName;
150: if (ix != -1) {
151: cName = fullName.substring(ix + 1);
152: }
153: if (pkg.startsWith("java.") || pkg.startsWith("javax.")) {
154: pkg = packageName(JavaProxyClassFactory.class)
155: + ".gen";
156: }
157: if (ix == -1) {
158: targetClassName = cName + "$Proxy" + nextId();
159: } else {
160: targetClassName = pkg + "." + cName + "$Proxy"
161: + nextId();
162: }
163: }
164:
165: validateArgs(targetClassName, super Class);
166:
167: Map methods = new HashMap();
168: collectMethods(super Class, interfaces, methods, names);
169:
170: Type selfType = Type.getType("L"
171: + toInternalClassName(targetClassName) + ";");
172: proxyClass = generate(loader, targetClassName, super Class,
173: interfaces, methods, selfType);
174:
175: proxies.put(key, proxyClass);
176: }
177:
178: return proxyClass;
179: }
180:
181: static JavaProxyClass newProxyClass(ClassLoader loader,
182: String targetClassName, Class super Class, Class[] interfaces)
183: throws InvocationTargetException {
184: return newProxyClass(loader, targetClassName, super Class,
185: interfaces, null);
186: }
187:
188: private static JavaProxyClass generate(final ClassLoader loader,
189: final String targetClassName, final Class super Class,
190: final Class[] interfaces, final Map methods,
191: final Type selfType) {
192: ClassWriter cw = beginProxyClass(targetClassName, super Class,
193: interfaces);
194:
195: GeneratorAdapter clazzInit = createClassInitializer(selfType,
196: cw);
197:
198: generateConstructors(super Class, selfType, cw);
199:
200: generateGetProxyClass(selfType, cw);
201:
202: generateGetInvocationHandler(selfType, cw);
203:
204: generateProxyMethods(super Class, methods, selfType, cw,
205: clazzInit);
206:
207: // finish class initializer
208: clazzInit.returnValue();
209: clazzInit.endMethod();
210:
211: // end class
212: cw.visitEnd();
213:
214: byte[] data = cw.toByteArray();
215:
216: /*
217: * try { FileOutputStream o = new
218: * FileOutputStream(targetClassName.replace( '/', '.') + ".class");
219: * o.write(data); o.close(); } catch (IOException ex) {
220: * ex.printStackTrace(); }
221: */
222:
223: Class clazz = invokeDefineClass(loader,
224: selfType.getClassName(), data);
225:
226: // trigger class initialization for the class
227: try {
228: Field proxy_class = clazz
229: .getDeclaredField(PROXY_CLASS_FIELD_NAME);
230: proxy_class.setAccessible(true);
231: return (JavaProxyClass) proxy_class.get(clazz);
232: } catch (Exception ex) {
233: InternalError ie = new InternalError();
234: ie.initCause(ex);
235: throw ie;
236: }
237: }
238:
239: static {
240: AccessController.doPrivileged(new PrivilegedAction() {
241: public Object run() {
242: try {
243: defineClass_method = ClassLoader.class
244: .getDeclaredMethod("defineClass",
245: new Class[] { String.class,
246: byte[].class, int.class,
247: int.class });
248: } catch (Exception e) {
249: // should not happen!
250: e.printStackTrace();
251: return null;
252: }
253: defineClass_method.setAccessible(true);
254: return null;
255: }
256: });
257: }
258:
259: private static Class invokeDefineClass(ClassLoader loader,
260: String className, byte[] data) {
261: try {
262: return (Class) defineClass_method.invoke(loader,
263: new Object[] { className, data, new Integer(0),
264: new Integer(data.length) });
265: } catch (IllegalArgumentException e) {
266: // TODO Auto-generated catch block
267: e.printStackTrace();
268: return null;
269: } catch (IllegalAccessException e) {
270: // TODO Auto-generated catch block
271: e.printStackTrace();
272: return null;
273: } catch (InvocationTargetException e) {
274: // TODO Auto-generated catch block
275: e.printStackTrace();
276: return null;
277: }
278: }
279:
280: private static ClassWriter beginProxyClass(
281: final String targetClassName, final Class super Class,
282: final Class[] interfaces) {
283:
284: ClassWriter cw = new ClassWriter(true);
285:
286: int access = Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL
287: | Opcodes.ACC_STATIC;
288: String name = toInternalClassName(targetClassName);
289: String signature = null;
290: String super name = toInternalClassName(super Class);
291: String[] interfaceNames = new String[interfaces.length + 1];
292: for (int i = 0; i < interfaces.length; i++) {
293: interfaceNames[i] = toInternalClassName(interfaces[i]);
294: }
295: interfaceNames[interfaces.length] = toInternalClassName(InternalJavaProxy.class);
296:
297: // start class
298: cw.visit(Opcodes.V1_3, access, name, signature, super name,
299: interfaceNames);
300:
301: cw.visitField(Opcodes.ACC_PRIVATE,
302: INVOCATION_HANDLER_FIELD_NAME,
303: INVOCATION_HANDLER_TYPE.getDescriptor(), null, null)
304: .visitEnd();
305:
306: cw.visitField(Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC,
307: PROXY_CLASS_FIELD_NAME,
308: PROXY_CLASS_TYPE.getDescriptor(), null, null)
309: .visitEnd();
310:
311: return cw;
312: }
313:
314: private static void generateProxyMethods(Class super Class,
315: Map methods, Type selfType, ClassVisitor cw,
316: GeneratorAdapter clazzInit) {
317: Iterator it = methods.values().iterator();
318: while (it.hasNext()) {
319: MethodData md = (MethodData) it.next();
320: Type super ClassType = Type.getType(super Class);
321: generateProxyMethod(selfType, super ClassType, cw,
322: clazzInit, md);
323: }
324: }
325:
326: private static void generateGetInvocationHandler(Type selfType,
327: ClassVisitor cw) {
328: // make getter for handler
329: GeneratorAdapter gh = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
330: new org.objectweb.asm.commons.Method(
331: "___getInvocationHandler",
332: INVOCATION_HANDLER_TYPE, EMPTY_TYPE_ARR), null,
333: EMPTY_TYPE_ARR, cw);
334:
335: gh.loadThis();
336: gh.getField(selfType, INVOCATION_HANDLER_FIELD_NAME,
337: INVOCATION_HANDLER_TYPE);
338: gh.returnValue();
339: gh.endMethod();
340: }
341:
342: private static void generateGetProxyClass(Type selfType,
343: ClassVisitor cw) {
344: // make getter for proxy class
345: GeneratorAdapter gpc = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
346: new org.objectweb.asm.commons.Method(
347: "___getProxyClass", PROXY_CLASS_TYPE,
348: EMPTY_TYPE_ARR), null, EMPTY_TYPE_ARR, cw);
349: gpc.getStatic(selfType, PROXY_CLASS_FIELD_NAME,
350: PROXY_CLASS_TYPE);
351: gpc.returnValue();
352: gpc.endMethod();
353: }
354:
355: private static void generateConstructors(Class super Class,
356: Type selfType, ClassVisitor cw) {
357: Constructor[] cons = super Class.getConstructors();
358: for (int i = 0; i < cons.length; i++) {
359: Constructor constructor = cons[i];
360:
361: int acc = constructor.getModifiers();
362: if (Modifier.isProtected(acc) || Modifier.isPublic(acc)) {
363: // ok, it's publix or protected
364: } else if (!Modifier.isPrivate(acc)
365: && packageName(constructor.getDeclaringClass())
366: .equals(
367: packageName(selfType.getClassName()))) {
368: // ok, it's package scoped and we're in the same package
369: } else {
370: // it's unaccessible
371: continue;
372: }
373:
374: generateConstructor(selfType, constructor, cw);
375: }
376: }
377:
378: private static GeneratorAdapter createClassInitializer(
379: Type selfType, ClassVisitor cw) {
380: GeneratorAdapter clazzInit;
381: clazzInit = new GeneratorAdapter(Opcodes.ACC_PRIVATE
382: | Opcodes.ACC_STATIC,
383: new org.objectweb.asm.commons.Method("<clinit>",
384: Type.VOID_TYPE, EMPTY_TYPE_ARR), null,
385: EMPTY_TYPE_ARR, cw);
386:
387: clazzInit.visitLdcInsn(selfType.getClassName());
388: clazzInit.invokeStatic(JAVA_LANG_CLASS_TYPE,
389: CLASS_FORNAME_METHOD);
390: clazzInit.invokeStatic(PROXY_HELPER_TYPE,
391: HELPER_GET_PROXY_CLASS_METHOD);
392: clazzInit.dup();
393: clazzInit.putStatic(selfType, PROXY_CLASS_FIELD_NAME,
394: PROXY_CLASS_TYPE);
395: return clazzInit;
396: }
397:
398: private static void generateProxyMethod(Type selfType,
399: Type super Type, ClassVisitor cw,
400: GeneratorAdapter clazzInit, MethodData md) {
401: if (!md.generateProxyMethod()) {
402: return;
403: }
404:
405: org.objectweb.asm.commons.Method m = md.getMethod();
406: Type[] ex = toType(md.getExceptions());
407:
408: String field_name = "__mth$" + md.getName()
409: + md.scrambledSignature();
410:
411: // create static private method field
412: FieldVisitor fv = cw.visitField(Opcodes.ACC_PRIVATE
413: | Opcodes.ACC_STATIC, field_name, PROXY_METHOD_TYPE
414: .getDescriptor(), null, null);
415: fv.visitEnd();
416:
417: clazzInit.dup();
418: clazzInit.push(m.getName());
419: clazzInit.push(m.getDescriptor());
420: clazzInit.push(md.isImplemented());
421: clazzInit.invokeStatic(PROXY_HELPER_TYPE,
422: PROXY_HELPER_GET_METHOD);
423: clazzInit.putStatic(selfType, field_name, PROXY_METHOD_TYPE);
424:
425: org.objectweb.asm.commons.Method sm = new org.objectweb.asm.commons.Method(
426: "__super$" + m.getName(), m.getReturnType(), m
427: .getArgumentTypes());
428:
429: //
430: // construct the proxy method
431: //
432: GeneratorAdapter ga = new GeneratorAdapter(Opcodes.ACC_PUBLIC,
433: m, null, ex, cw);
434:
435: ga.loadThis();
436: ga.getField(selfType, INVOCATION_HANDLER_FIELD_NAME,
437: INVOCATION_HANDLER_TYPE);
438:
439: // if the method is extending something, then we have
440: // to test if the handler is initialized...
441:
442: if (md.isImplemented()) {
443: ga.dup();
444: Label ok = ga.newLabel();
445: ga.ifNonNull(ok);
446:
447: ga.loadThis();
448: ga.loadArgs();
449: ga.invokeConstructor(super Type, m);
450: ga.returnValue();
451: ga.mark(ok);
452: }
453:
454: ga.loadThis();
455: ga.getStatic(selfType, field_name, PROXY_METHOD_TYPE);
456:
457: if (m.getArgumentTypes().length == 0) {
458: // load static empty array
459: ga.getStatic(JAVA_PROXY_TYPE, "NO_ARGS", Type
460: .getType(Object[].class));
461: } else {
462: // box arguments
463: ga.loadArgArray();
464: }
465:
466: Label before = ga.mark();
467:
468: ga.invokeInterface(INVOCATION_HANDLER_TYPE,
469: INVOCATION_HANDLER_INVOKE_METHOD);
470:
471: Label after = ga.mark();
472:
473: ga.unbox(m.getReturnType());
474: ga.returnValue();
475:
476: // this is a simple rethrow handler
477: Label rethrow = ga.mark();
478: ga.visitInsn(Opcodes.ATHROW);
479:
480: for (int i = 0; i < ex.length; i++) {
481: ga.visitTryCatchBlock(before, after, rethrow, ex[i]
482: .getInternalName());
483: }
484:
485: ga
486: .visitTryCatchBlock(before, after, rethrow,
487: "java/lang/Error");
488: ga.visitTryCatchBlock(before, after, rethrow,
489: "java/lang/RuntimeException");
490:
491: Type thr = Type.getType(Throwable.class);
492: Label handler = ga.mark();
493: Type udt = Type.getType(UndeclaredThrowableException.class);
494: int loc = ga.newLocal(thr);
495: ga.storeLocal(loc, thr);
496: ga.newInstance(udt);
497: ga.dup();
498: ga.loadLocal(loc, thr);
499: ga.invokeConstructor(udt, org.objectweb.asm.commons.Method
500: .getMethod("void <init>(java.lang.Throwable)"));
501: ga.throwException();
502:
503: ga.visitTryCatchBlock(before, after, handler,
504: "java/lang/Throwable");
505:
506: ga.endMethod();
507:
508: //
509: // construct the super-proxy method
510: //
511: if (md.isImplemented()) {
512:
513: GeneratorAdapter ga2 = new GeneratorAdapter(
514: Opcodes.ACC_PUBLIC, sm, null, ex, cw);
515:
516: ga2.loadThis();
517: ga2.loadArgs();
518: ga2.invokeConstructor(super Type, m);
519: ga2.returnValue();
520: ga2.endMethod();
521: }
522: }
523:
524: private static Class[] generateConstructor(Type selfType,
525: Constructor constructor, ClassVisitor cw) {
526:
527: Class[] super ConstructorParameterTypes = constructor
528: .getParameterTypes();
529: Class[] newConstructorParameterTypes = new Class[super ConstructorParameterTypes.length + 1];
530: System.arraycopy(super ConstructorParameterTypes, 0,
531: newConstructorParameterTypes, 0,
532: super ConstructorParameterTypes.length);
533: newConstructorParameterTypes[super ConstructorParameterTypes.length] = JavaProxyInvocationHandler.class;
534:
535: int access = Opcodes.ACC_PUBLIC;
536: String name1 = "<init>";
537: String signature = null;
538: Class[] super ConstructorExceptions = constructor
539: .getExceptionTypes();
540:
541: org.objectweb.asm.commons.Method super _m = new org.objectweb.asm.commons.Method(
542: name1, Type.VOID_TYPE,
543: toType(super ConstructorParameterTypes));
544: org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method(
545: name1, Type.VOID_TYPE,
546: toType(newConstructorParameterTypes));
547:
548: GeneratorAdapter ga = new GeneratorAdapter(access, m,
549: signature, toType(super ConstructorExceptions), cw);
550:
551: ga.loadThis();
552: ga.loadArgs(0, super ConstructorParameterTypes.length);
553: ga.invokeConstructor(Type.getType(constructor
554: .getDeclaringClass()), super _m);
555:
556: ga.loadThis();
557: ga.loadArg(super ConstructorParameterTypes.length);
558: ga.putField(selfType, INVOCATION_HANDLER_FIELD_NAME,
559: INVOCATION_HANDLER_TYPE);
560:
561: // do a void return
562: ga.returnValue();
563: ga.endMethod();
564: return newConstructorParameterTypes;
565: }
566:
567: private static String toInternalClassName(Class clazz) {
568: return toInternalClassName(clazz.getName());
569: }
570:
571: private static String toInternalClassName(String name) {
572: return name.replace('.', '/');
573: }
574:
575: private static Type[] toType(Class[] parameterTypes) {
576: Type[] result = new Type[parameterTypes.length];
577: for (int i = 0; i < result.length; i++) {
578: result[i] = Type.getType(parameterTypes[i]);
579: }
580: return result;
581: }
582:
583: private static void collectMethods(Class super Class,
584: Class[] interfaces, Map methods, Set names) {
585: HashSet allClasses = new HashSet();
586: addClass(allClasses, methods, super Class, names);
587: addInterfaces(allClasses, methods, interfaces, names);
588: }
589:
590: static class MethodData {
591: Set methods = new HashSet();
592:
593: final Method mostSpecificMethod;
594: final Class[] mostSpecificParameterTypes;
595:
596: boolean hasPublicDecl = false;
597:
598: MethodData(Method method) {
599: this .mostSpecificMethod = method;
600: this .mostSpecificParameterTypes = mostSpecificMethod
601: .getParameterTypes();
602: hasPublicDecl = method.getDeclaringClass().isInterface()
603: || Modifier.isPublic(method.getModifiers());
604: }
605:
606: public String scrambledSignature() {
607: StringBuffer sb = new StringBuffer();
608: Class[] parms = getParameterTypes();
609: for (int i = 0; i < parms.length; i++) {
610: sb.append('$');
611: String name = parms[i].getName();
612: name = name.replace('[', '1');
613: name = name.replace('.', '_');
614: name = name.replace(';', '2');
615: sb.append(name);
616: }
617: return sb.toString();
618: }
619:
620: public Class getDeclaringClass() {
621: return mostSpecificMethod.getDeclaringClass();
622: }
623:
624: public org.objectweb.asm.commons.Method getMethod() {
625: return new org.objectweb.asm.commons.Method(getName(), Type
626: .getType(getReturnType()),
627: getType(getParameterTypes()));
628: }
629:
630: private Type[] getType(Class[] parameterTypes) {
631: Type[] result = new Type[parameterTypes.length];
632: for (int i = 0; i < parameterTypes.length; i++) {
633: result[i] = Type.getType(parameterTypes[i]);
634: }
635: return result;
636: }
637:
638: private String getName() {
639: return mostSpecificMethod.getName();
640: }
641:
642: private Class[] getParameterTypes() {
643: return mostSpecificParameterTypes;
644: }
645:
646: public Class[] getExceptions() {
647:
648: Set all = new HashSet();
649:
650: Iterator it = methods.iterator();
651: while (it.hasNext()) {
652: Method m = (Method) it.next();
653: Class[] ex = m.getExceptionTypes();
654: for (int i = 0; i < ex.length; i++) {
655: Class exx = ex[i];
656:
657: if (all.contains(exx)) {
658: continue;
659: }
660:
661: boolean add = true;
662: Iterator it2 = all.iterator();
663: while (it2.hasNext()) {
664: Class de = (Class) it2.next();
665:
666: if (de.isAssignableFrom(exx)) {
667: add = false;
668: break;
669: } else if (exx.isAssignableFrom(de)) {
670: it2.remove();
671: add = true;
672: }
673:
674: }
675:
676: if (add) {
677: all.add(exx);
678: }
679: }
680: }
681:
682: return (Class[]) all.toArray(new Class[all.size()]);
683: }
684:
685: public boolean generateProxyMethod() {
686: return !isFinal() && !isPrivate();
687: }
688:
689: public void add(Method method) {
690: methods.add(method);
691: hasPublicDecl |= Modifier.isPublic(method.getModifiers());
692: }
693:
694: Class getReturnType() {
695: return mostSpecificMethod.getReturnType();
696: }
697:
698: boolean isFinal() {
699: if (mostSpecificMethod.getDeclaringClass().isInterface()) {
700: return false;
701: }
702:
703: int mod = mostSpecificMethod.getModifiers();
704: return Modifier.isFinal(mod);
705: }
706:
707: boolean isPrivate() {
708: if (mostSpecificMethod.getDeclaringClass().isInterface()) {
709: return false;
710: }
711:
712: int mod = mostSpecificMethod.getModifiers();
713: return Modifier.isPrivate(mod);
714: }
715:
716: boolean isImplemented() {
717: if (mostSpecificMethod.getDeclaringClass().isInterface()) {
718: return false;
719: }
720:
721: int mod = mostSpecificMethod.getModifiers();
722: return !Modifier.isAbstract(mod);
723: }
724: }
725:
726: static class MethodKey {
727: private String name;
728:
729: private Class[] arguments;
730:
731: MethodKey(Method m) {
732: this .name = m.getName();
733: this .arguments = m.getParameterTypes();
734: }
735:
736: public boolean equals(Object obj) {
737: if (obj instanceof MethodKey) {
738: MethodKey key = (MethodKey) obj;
739:
740: return name.equals(key.name)
741: && Arrays.equals(arguments, key.arguments);
742: }
743:
744: return false;
745: }
746:
747: public int hashCode() {
748: return name.hashCode();
749: }
750: }
751:
752: private static void addInterfaces(Set allClasses, Map methods,
753: Class[] interfaces, Set names) {
754: for (int i = 0; i < interfaces.length; i++) {
755: addInterface(allClasses, methods, interfaces[i], names);
756: }
757: }
758:
759: private static void addInterface(Set allClasses, Map methods,
760: Class interfaze, Set names) {
761: if (allClasses.add(interfaze)) {
762: addMethods(methods, interfaze, names);
763: addInterfaces(allClasses, methods, interfaze
764: .getInterfaces(), names);
765: }
766: }
767:
768: private static void addMethods(Map methods, Class classOrInterface,
769: Set names) {
770: Method[] mths = classOrInterface.getDeclaredMethods();
771: for (int i = 0; i < mths.length; i++) {
772: if (names == null || names.contains(mths[i].getName())) {
773: addMethod(methods, mths[i]);
774: }
775: }
776: }
777:
778: private static void addMethod(Map methods, Method method) {
779: int acc = method.getModifiers();
780:
781: if (Modifier.isStatic(acc) || Modifier.isPrivate(acc)) {
782: return;
783: }
784:
785: MethodKey mk = new MethodKey(method);
786: MethodData md = (MethodData) methods.get(mk);
787: if (md == null) {
788: md = new MethodData(method);
789: methods.put(mk, md);
790: }
791: md.add(method);
792: }
793:
794: private static void addClass(Set allClasses, Map methods,
795: Class clazz, Set names) {
796: if (allClasses.add(clazz)) {
797: addMethods(methods, clazz, names);
798: Class super Class = clazz.getSuperclass();
799: if (super Class != null) {
800: addClass(allClasses, methods, super Class, names);
801: }
802:
803: addInterfaces(allClasses, methods, clazz.getInterfaces(),
804: names);
805: }
806: }
807:
808: private static void validateArgs(String targetClassName,
809: Class super Class) {
810:
811: if (Modifier.isFinal(super Class.getModifiers())) {
812: throw new IllegalArgumentException(
813: "cannot extend final class");
814: }
815:
816: String targetPackage = packageName(targetClassName);
817:
818: String pkg = targetPackage.replace('.', '/');
819: if (pkg.startsWith("java")) {
820: throw new IllegalArgumentException(
821: "cannor add classes to package " + pkg);
822: }
823:
824: Package p = Package.getPackage(pkg);
825: if (p != null) {
826: if (p.isSealed()) {
827: throw new IllegalArgumentException("package " + p
828: + " is sealed");
829: }
830: }
831: }
832:
833: private static String packageName(Class clazz) {
834: String clazzName = clazz.getName();
835: return packageName(clazzName);
836: }
837:
838: private static String packageName(String clazzName) {
839: int idx = clazzName.lastIndexOf('.');
840: if (idx == -1) {
841: return "";
842: } else {
843: return clazzName.substring(0, idx);
844: }
845: }
846:
847: }
|