0001: /*
0002: * @(#)DynamicRuntime.java 1.7 05/05/09
0003: *
0004: * Copyright (c) 1997-2005 Sun Microsystems, Inc. All Rights Reserved.
0005: *
0006: * See the file "LICENSE.txt" for information on usage and redistribution
0007: * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
0008: */
0009: package pnuts.compiler;
0010:
0011: import java.lang.reflect.Constructor;
0012: import java.lang.reflect.Field;
0013: import java.lang.reflect.InvocationTargetException;
0014: import java.lang.reflect.Method;
0015: import java.lang.reflect.Modifier;
0016: import java.security.AccessController;
0017: import java.security.PrivilegedAction;
0018: import java.util.Map;
0019: import java.util.HashMap;
0020: import java.util.WeakHashMap;
0021: import java.io.IOException;
0022:
0023: import org.pnuts.util.*;
0024: import org.pnuts.lang.*;
0025:
0026: import pnuts.lang.Context;
0027: import pnuts.lang.Pnuts;
0028: import pnuts.lang.PnutsException;
0029: import pnuts.lang.PnutsFunction;
0030: import pnuts.lang.Runtime;
0031:
0032: public class DynamicRuntime extends Runtime {
0033: private final static boolean DEBUG = false;
0034: private final static Object[] noarg = new Object[] {};
0035:
0036: private CodeLoader codeLoader = null;
0037: static boolean hasCollection = false;
0038: static {
0039: try {
0040: Class.forName("java.util.Collection");
0041: hasCollection = true;
0042: } catch (Exception e) {
0043: }
0044: }
0045:
0046: /**
0047: * call a method
0048: */
0049: protected Object _callMethod(Context context, Class c, String name,
0050: Object args[], Class types[], Object target) {
0051: try {
0052: ClassLoader cl = codeLoader;
0053: synchronized (this ) {
0054: if (cl == null) {
0055: cl = Compiler.createCodeLoader(context
0056: .getClassLoader(), true);
0057: codeLoader = (CodeLoader) cl;
0058: }
0059: }
0060: return callMethod(context, c, name, args, types, target,
0061: (CodeLoader) cl);
0062: } catch (IllegalAccessException e0) {
0063: throw new PnutsException(e0, context);
0064: } catch (InvocationTargetException e1) {
0065: Throwable t = e1.getTargetException();
0066: if (t instanceof PnutsException) {
0067: throw (PnutsException) t;
0068: } else {
0069: throw new PnutsException(t, context);
0070: }
0071: } catch (NoMemberFoundException e2) {
0072: throw new PnutsException("method.notFound", new Object[] {
0073: name, c.getName(), Pnuts.format(args) }, context);
0074: } catch (PnutsException e3) {
0075: throw e3;
0076: } catch (Throwable e4) {
0077: throw new PnutsException(e4, context);
0078: }
0079: }
0080:
0081: Object callMethod(Context context, Class c, String name,
0082: Object args[], Class types[], Object target,
0083: CodeLoader codeLoader) throws IllegalAccessException,
0084: IllegalArgumentException, InvocationTargetException {
0085: ProxyCache methodCache = null;
0086:
0087: if (name == CLONE && args.length == 0) {
0088: if (target instanceof Object[]) {
0089: return ((Object[]) target).clone();
0090: } else if (target instanceof int[]) {
0091: return ((int[]) target).clone();
0092: } else if (target instanceof byte[]) {
0093: return ((byte[]) target).clone();
0094: } else if (target instanceof short[]) {
0095: return ((short[]) target).clone();
0096: } else if (target instanceof char[]) {
0097: return ((char[]) target).clone();
0098: } else if (target instanceof long[]) {
0099: return ((long[]) target).clone();
0100: } else if (target instanceof float[]) {
0101: return ((float[]) target).clone();
0102: } else if (target instanceof double[]) {
0103: return ((double[]) target).clone();
0104: } else if (target instanceof boolean[]) {
0105: return ((boolean[]) target).clone();
0106: }
0107: }
0108:
0109: ProxyCache[] m = methodFinder.getMethods(context, c, name);
0110: if (m == null) {
0111: return super ._callMethod(context, c, name, args, types,
0112: target);
0113: }
0114:
0115: int count = 0;
0116: int min = Integer.MAX_VALUE;
0117: Stack methods = new Stack();
0118: int nargs = args.length;
0119: cand: for (int i = 0; i < m.length; i++) {
0120: Class p[] = m[i].paramTypes;
0121: if (p.length != nargs) {
0122: continue;
0123: }
0124: count = 0;
0125: for (int j = 0; j < p.length; j++) {
0126: Class pj = p[j];
0127: if (types != null) {
0128: Class tj = types[j];
0129: if (tj != null && pj != tj
0130: && !pj.isAssignableFrom(tj)) {
0131: continue cand;
0132: }
0133: }
0134: int t = matchType(pj, args[j]);
0135: if (t < 0) {
0136: continue cand;
0137: }
0138: count += t;
0139: }
0140: if (count > min) {
0141: continue;
0142: }
0143:
0144: if ((m[i].type == ProxyCache.STATIC) != (target == null)) {
0145: continue;
0146: }
0147:
0148: if (count < min) {
0149: methods.removeAllElements();
0150: methods.push(m[i]);
0151: min = count;
0152: } else if (count == min) {
0153: methods.push(m[i]);
0154: if (DEBUG) {
0155: System.out.println("push " + m[i]);
0156: }
0157: }
0158: }
0159:
0160: Class clazz = c;
0161: out: while (clazz != null) {
0162: int size = methods.size();
0163: for (int i = 0; i < size; i++) {
0164: methodCache = (ProxyCache) methods.pop();
0165: if (methodCache.declaringClass == clazz) {
0166: break out;
0167: }
0168: }
0169: clazz = clazz.getSuperclass();
0170: }
0171:
0172: if (methodCache != null) {
0173: boolean retry = false;
0174: while (true) {
0175: try {
0176: if (methodCache.proxy == null || retry) {
0177: methodCache.proxy = DynamicProxyFactory
0178: .makeProxy(name,
0179: methodCache.targetClass,
0180: methodCache.returnType,
0181: methodCache.paramTypes,
0182: methodCache.type, codeLoader);
0183: }
0184: if (nargs == 0) {
0185: return methodCache.invoke(target);
0186: } else {
0187: return methodCache.invoke(target, args);
0188: }
0189: } catch (ClassCastException cce) {
0190: if (!retry) {
0191: codeLoader = Compiler.createCodeLoader(
0192: getClassLoader(c), true);
0193: if (DEBUG) {
0194: System.out.println("retry");
0195: }
0196: retry = true;
0197: continue;
0198: }
0199: if (DEBUG) {
0200: System.out.println("use reflection");
0201: }
0202: methodFinder.useReflection(c);
0203: return super ._callMethod(context, c, name, args,
0204: types, target);
0205: } catch (LinkageError err) {
0206: if (DEBUG) {
0207: System.out.println(err + " , " + codeLoader);
0208: }
0209: if (!retry) {
0210: codeLoader = Compiler.createCodeLoader(
0211: getClassLoader(c), true);
0212: if (DEBUG) {
0213: System.out.println("retry");
0214: }
0215: retry = true;
0216: continue;
0217: }
0218: if (DEBUG) {
0219: System.out.println("use reflection");
0220: }
0221: methodFinder.useReflection(c);
0222: return super ._callMethod(context, c, name, args,
0223: types, target);
0224: } catch (PnutsException pex) {
0225: throw pex;
0226: } catch (Throwable t) {
0227: if (t instanceof ClassNotFoundException) {
0228: if (DEBUG) {
0229: System.out.println(t);
0230: }
0231: if (DEBUG) {
0232: System.out.println("use reflection");
0233: }
0234: methodFinder.useReflection(c);
0235: return super ._callMethod(context, c, name,
0236: args, types, target);
0237: } else {
0238: throw new PnutsException(t, context);
0239: }
0240: }
0241: }
0242: } else {
0243: if (target instanceof Class) {
0244: return callMethod(context, (Class) target, name, args,
0245: types, null, codeLoader);
0246: }
0247: throw new NoMemberFoundException();
0248: }
0249: }
0250:
0251: protected Object _callConstructor(Context context, Class c,
0252: Object args[], Class types[]) {
0253: try {
0254: ClassLoader cl = codeLoader;
0255: synchronized (this ) {
0256: if (cl == null) {
0257: cl = Compiler.createCodeLoader(context
0258: .getClassLoader(), true);
0259: codeLoader = (CodeLoader) cl;
0260: }
0261: }
0262: return _callConstructor(context, c, args, types,
0263: (CodeLoader) cl);
0264:
0265: } catch (InvocationTargetException e1) {
0266: Throwable t = ((InvocationTargetException) e1)
0267: .getTargetException();
0268: if (t instanceof PnutsException) {
0269: throw (PnutsException) t;
0270: } else {
0271: throw new PnutsException(t, context);
0272: }
0273: } catch (NoMemberFoundException e2) {
0274: throw new PnutsException("constructor.notFound",
0275: new Object[] { c, Pnuts.format(args) }, context);
0276: } catch (PnutsException e3) {
0277: throw e3;
0278: } catch (Throwable e4) {
0279: throw new PnutsException(e4, context);
0280: }
0281: }
0282:
0283: protected Object _callConstructor(Context context, Class c,
0284: Object args[], Class types[], CodeLoader codeLoader)
0285: throws IllegalAccessException, IllegalArgumentException,
0286: InvocationTargetException, InstantiationException {
0287: ProxyCache cs[] = methodFinder.getConstructors(context, c);
0288: ProxyCache cons = null;
0289:
0290: if (cs == null) {
0291: return super ._callConstructor(context, c, args, types);
0292: }
0293:
0294: int count = 0;
0295: int min = Integer.MAX_VALUE;
0296: cand: for (int i = 0; i < cs.length; i++) {
0297: Class p[] = cs[i].paramTypes;
0298: if (p.length != args.length) {
0299: continue;
0300: }
0301: count = 0;
0302: for (int j = 0; j < p.length; j++) {
0303: Class pj = p[j];
0304: if (types != null) {
0305: Class tj = types[j];
0306: if (tj != null && pj != tj
0307: && !pj.isAssignableFrom(tj)) {
0308: continue cand;
0309: }
0310: }
0311: int t = matchType(pj, args[j]);
0312: if (t < 0) {
0313: continue cand;
0314: }
0315: count += t;
0316: }
0317: if (count < min) {
0318: min = count;
0319: cons = cs[i];
0320: }
0321: }
0322: if (cons != null) {
0323: boolean retry = false;
0324: while (true) {
0325: if (cons.proxy == null || retry) {
0326: cons.proxy = DynamicProxyFactory.makeProxy(
0327: "<init>", cons.declaringClass,
0328: cons.returnType, cons.paramTypes,
0329: ProxyCache.CONSTRUCTOR, codeLoader);
0330: }
0331: try {
0332: if (args.length == 0) {
0333: return cons.invoke(null);
0334: } else {
0335: return cons.invoke(null, args);
0336: }
0337: } catch (ClassCastException cce) {
0338: if (DEBUG) {
0339: System.out.println(cce);
0340: }
0341: if (!retry) {
0342: codeLoader = Compiler.createCodeLoader(
0343: getClassLoader(c), true);
0344: if (DEBUG) {
0345: System.out.println("retry");
0346: }
0347: retry = true;
0348: continue;
0349: }
0350: methodFinder.useReflection(c);
0351: return super ._callConstructor(context, c, args,
0352: types);
0353: } catch (LinkageError err) {
0354: if (DEBUG) {
0355: System.out.println(err);
0356: }
0357: if (!retry) {
0358: codeLoader = Compiler.createCodeLoader(
0359: getClassLoader(c), true);
0360: if (DEBUG) {
0361: System.out.println("retry");
0362: }
0363: retry = true;
0364: continue;
0365: }
0366: methodFinder.useReflection(c);
0367: return super ._callConstructor(context, c, args,
0368: types);
0369: } catch (PnutsException pex) {
0370: throw pex;
0371: } catch (Throwable t) {
0372: /* jdk1.1.8 throws ClassNotFoundException instead of NoClassDefFoundError */
0373: if (DEBUG) {
0374: System.out.println(t);
0375: }
0376: if (t instanceof ClassNotFoundException
0377: || t instanceof IllegalAccessError) {
0378: methodFinder.useReflection(c);
0379: return super ._callConstructor(context, c, args,
0380: types);
0381: } else {
0382: throw new PnutsException(t, context);
0383: }
0384: }
0385: }
0386: } else {
0387: throw new NoMemberFoundException();
0388: }
0389: }
0390:
0391: static class ProxyCache {
0392: final static int DEFAULT = 0;
0393: final static int STATIC = 1;
0394: final static int CONSTRUCTOR = 2;
0395:
0396: Class[] paramTypes;
0397: Class declaringClass;
0398: Class targetClass;
0399: Class returnType;
0400: int type;
0401: DynamicProxy proxy;
0402: boolean hasArrayParam;
0403:
0404: ProxyCache(Method method, Class clazz) {
0405: this .paramTypes = method.getParameterTypes();
0406: this .declaringClass = method.getDeclaringClass();
0407: this .returnType = method.getReturnType();
0408: this .type = Modifier.isStatic(method.getModifiers()) ? STATIC
0409: : DEFAULT;
0410: this .targetClass = clazz;
0411: init();
0412: }
0413:
0414: ProxyCache(Constructor cons) {
0415: this .paramTypes = cons.getParameterTypes();
0416: this .declaringClass = cons.getDeclaringClass();
0417: this .targetClass = cons.getDeclaringClass();
0418: this .returnType = void.class;
0419: this .type = CONSTRUCTOR;
0420: init();
0421: }
0422:
0423: void init() {
0424: Class[] types = this .paramTypes;
0425: for (int i = 0; i < types.length; i++) {
0426: if (types[i].isArray()) {
0427: hasArrayParam = true;
0428: break;
0429: }
0430: }
0431: }
0432:
0433: Object invoke(Object target) {
0434: return proxy.invoke(target);
0435: }
0436:
0437: Object invoke(Object target, Object[] args) {
0438: if (hasArrayParam) {
0439: for (int i = 0; i < paramTypes.length; i++) {
0440: Class type = paramTypes[i];
0441: Object arg = args[i];
0442: if (arg != null && type.isArray()
0443: && !type.isInstance(arg)) {
0444: args[i] = transform(type, arg);
0445: }
0446: }
0447: }
0448: return proxy.invoke(target, args);
0449: }
0450: }
0451:
0452: static abstract class MethodFinder {
0453: public abstract ProxyCache[] getConstructors(Context context,
0454: Class c);
0455:
0456: public abstract ProxyCache[] getMethods(Context context,
0457: Class c, String name);
0458:
0459: public abstract void useReflection(Class c);
0460: }
0461:
0462: static class Java2MethodFinder extends MethodFinder {
0463: private Map mtab = Runtime.createWeakMap();
0464: private Map ctab = Runtime.createWeakMap();
0465:
0466: private static java.lang.ref.SoftReference USE_REFLECTION = new java.lang.ref.SoftReference(
0467: Runtime.createWeakMap());
0468:
0469: public void useReflection(Class c) {
0470: if (DEBUG) {
0471: System.out.println("use reflection for " + c.getName());
0472: }
0473: mtab.put(c, USE_REFLECTION);
0474: ctab.put(c, USE_REFLECTION);
0475: }
0476:
0477: public ProxyCache[] getMethods(Context context, Class c,
0478: String name) {
0479: java.lang.ref.SoftReference cache = (java.lang.ref.SoftReference) mtab
0480: .get(c);
0481: Map map;
0482: if (cache == null || (map = (Map) cache.get()) == null) {
0483: map = Runtime.createWeakMap();
0484: mtab.put(c, new java.lang.ref.SoftReference(map));
0485: } else if (cache == USE_REFLECTION) {
0486: return null;
0487: }
0488: Object v = map.get(name);
0489: if (v instanceof ProxyCache[]) {
0490: return (ProxyCache[]) v;
0491: } else {
0492: if (DEBUG) {
0493: System.out.println(c + ":" + name);
0494: }
0495: // boolean isPublic = Modifier.isPublic(c.getModifiers());
0496:
0497: Method m[] = Runtime.getMethods(context, c);
0498: if (m == null) { // for Bug:4137722
0499: throw new NoClassDefFoundError("" + c);
0500: }
0501: int j = 0;
0502: for (int i = 0; i < m.length; i++) {
0503: String m_name = m[i].getName();
0504: if (m_name.equals(name) && i >= j) {
0505: m[j] = m[i];
0506: j++;
0507: }
0508: }
0509: ProxyCache px[] = new ProxyCache[j];
0510: for (int i = 0; i < j; i++) {
0511: Method mi = findCallableMethod(c, m[i].getName(),
0512: m[i].getParameterTypes());
0513: if (mi != null) {
0514: px[i] = new ProxyCache(mi, c);
0515: } else {
0516: px[i] = new ProxyCache(m[i], c);
0517: }
0518: }
0519: map.put(name, px);
0520: return px;
0521: }
0522: }
0523:
0524: public ProxyCache[] getConstructors(Context context, Class c) {
0525:
0526: java.lang.ref.SoftReference cache = (java.lang.ref.SoftReference) ctab
0527: .get(c);
0528: ProxyCache[] pc;
0529: if (cache == USE_REFLECTION) {
0530: return null;
0531: } else if (cache == null
0532: || (pc = (ProxyCache[]) cache.get()) == null) {
0533: Constructor con[] = Runtime.getConstructors(context, c);
0534: ProxyCache px[] = new ProxyCache[con.length];
0535: for (int i = 0; i < con.length; i++) {
0536: px[i] = new ProxyCache(con[i]);
0537: }
0538: ctab.put(c, new java.lang.ref.SoftReference(px));
0539: return px;
0540: } else {
0541: return pc;
0542: }
0543: }
0544: }
0545:
0546: /**
0547: * This method maps a proxy object of a Constructor to a PnutsFunction.
0548: * Call of the resulting function is faster than reflection API calls.
0549: *
0550: * @param cons a constructor
0551: * @return an instance the constructor creates.
0552: */
0553: public static PnutsFunction makeProxy(Constructor cons) {
0554: final DynamicProxy px = DynamicProxyFactory.makeProxy(cons,
0555: Compiler.createCodeLoader(getClassLoader(cons
0556: .getDeclaringClass()), true));
0557:
0558: if (cons.getParameterTypes().length == 0) {
0559: return new PnutsFunction() {
0560: public Object exec(Object[] args, Context context) {
0561: return px.invoke(null);
0562: }
0563: };
0564: } else {
0565: return new PnutsFunction() {
0566: public Object exec(Object[] args, Context context) {
0567: return px.invoke(null, args);
0568: }
0569: };
0570: }
0571: }
0572:
0573: /**
0574: * This method maps a proxy object of a Method to a PnutsFunction.
0575: * Call of the resulting function is expected to be faster than
0576: * reflection API calls.
0577: *
0578: * @param method a method
0579: * @return the result of the 'method' call.
0580: */
0581: public static PnutsFunction makeProxy(Method method) {
0582: final DynamicProxy px = DynamicProxyFactory.makeProxy(method,
0583: Compiler.createCodeLoader(getClassLoader(method
0584: .getDeclaringClass()), true));
0585: final String _name = method.getName();
0586: boolean _static = Modifier.isStatic(method.getModifiers());
0587: int nargs = method.getParameterTypes().length;
0588:
0589: if (_static) {
0590: if (nargs == 0) {
0591: return new PnutsFunction() {
0592: public String getName() {
0593: return _name;
0594: }
0595:
0596: public Object exec(Object[] args, Context context) {
0597: return px.invoke(null);
0598: }
0599: };
0600: } else {
0601: return new PnutsFunction() {
0602: public String getName() {
0603: return _name;
0604: }
0605:
0606: public Object exec(Object[] args, Context context) {
0607: return px.invoke(null, args);
0608: }
0609: };
0610: }
0611: } else {
0612: if (nargs == 0) {
0613: return new PnutsFunction() {
0614: public String getName() {
0615: return _name;
0616: }
0617:
0618: public Object exec(Object[] args, Context context) {
0619: return px.invoke(args[0]);
0620: }
0621: };
0622: } else {
0623: return new PnutsFunction() {
0624: public String getName() {
0625: return _name;
0626: }
0627:
0628: public Object exec(Object[] args, Context context) {
0629: Object target = args[0];
0630: System.arraycopy(args, 1, args, 0,
0631: args.length - 1);
0632: return px.invoke(target, args);
0633: }
0634: };
0635: }
0636: }
0637: }
0638:
0639: static ClassLoader getClassLoader(final Class clazz) {
0640: if (Compiler.hasJava2Security) {
0641: return (ClassLoader) AccessController
0642: .doPrivileged(new PrivilegedAction() {
0643: public Object run() {
0644: return clazz.getClassLoader();
0645: }
0646: });
0647: } else {
0648: return clazz.getClassLoader();
0649: }
0650: }
0651:
0652: static class NoMemberFoundException extends RuntimeException {
0653: }
0654:
0655: static MethodFinder methodFinder = new Java2MethodFinder();
0656:
0657: private Map beanAccessors = Runtime.createWeakMap();
0658:
0659: static class BeanInfoParam {
0660: Class targetClass;
0661:
0662: Class stopClass;
0663:
0664: BeanInfoParam(Class targetClass, Class stopClass) {
0665: this .targetClass = targetClass;
0666: this .stopClass = stopClass;
0667: }
0668:
0669: public int hashCode() {
0670: return targetClass.hashCode() ^ stopClass.hashCode();
0671: }
0672:
0673: public boolean equals(Object that) {
0674: if (that instanceof BeanInfoParam) {
0675: BeanInfoParam p = (BeanInfoParam) that;
0676: return p.targetClass == this .targetClass
0677: && p.stopClass == this .stopClass;
0678: }
0679: return false;
0680: }
0681: }
0682:
0683: private Accessor getAccessor(Class cls, Class stopClass) {
0684: Object key;
0685: if (stopClass == null) {
0686: key = cls;
0687: } else {
0688: key = new BeanInfoParam(cls, stopClass);
0689: }
0690: java.lang.ref.SoftReference ref = (java.lang.ref.SoftReference) beanAccessors
0691: .get(key);
0692: if (ref == null) {
0693: Accessor a = createBeanAccessor(cls, stopClass);
0694: beanAccessors.put(key, new java.lang.ref.SoftReference(a));
0695: return a;
0696: } else {
0697: Accessor a = (Accessor) ref.get();
0698: if (a == null) {
0699: a = createBeanAccessor(cls, stopClass);
0700: beanAccessors.put(key, new java.lang.ref.SoftReference(
0701: a));
0702: }
0703: return a;
0704: }
0705: }
0706:
0707: private Accessor createBeanAccessor(Class cls, Class stopClass) {
0708: return new DynamicAccessor(cls, stopClass);
0709: }
0710:
0711: protected static class DynamicAccessor extends
0712: pnuts.lang.Runtime.Accessor {
0713: boolean isPublic;
0714:
0715: protected DynamicAccessor(Class cls, Class stopClass) {
0716: super (cls, stopClass);
0717: this .isPublic = Modifier.isPublic(cls.getModifiers());
0718: }
0719:
0720: public void addReadMethod(String name, Object method) {
0721: Method m = (Method) method;
0722: Method m0 = m;
0723: if (!isPublic) {
0724: String methodName = m.getName();
0725: Class[] types = m.getParameterTypes();
0726: m = findCallableMethod(beanClass, methodName, types);
0727: }
0728: if (m != null) {
0729: CodeLoader loader = Compiler.createCodeLoader(
0730: getClassLoader(m.getDeclaringClass()), true);
0731: DynamicProxy px = DynamicProxyFactory.makeProxy(m,
0732: loader);
0733: ProxyCache pc = new ProxyCache(m, beanClass);
0734: pc.proxy = px;
0735: super .addReadMethod(name, pc);
0736: } else {
0737: DynamicProxy px = createReflectionProxy(beanClass,
0738: name, stopClass);
0739: ProxyCache pc = new ProxyCache(m0, beanClass);
0740: pc.proxy = px;
0741: super .addReadMethod(name, pc);
0742: super .addWriteMethod(name, pc);
0743: }
0744: }
0745:
0746: public void addWriteMethod(String name, Object method) {
0747: Method m = (Method) method;
0748: Method m0 = m;
0749: if (!isPublic) {
0750: String methodName = m.getName();
0751: Class[] types = m.getParameterTypes();
0752: m = findCallableMethod(beanClass, methodName, types);
0753: }
0754: if (m != null) {
0755: CodeLoader loader = Compiler.createCodeLoader(
0756: getClassLoader(m.getDeclaringClass()), true);
0757: DynamicProxy px = DynamicProxyFactory.makeProxy(m,
0758: loader);
0759: ProxyCache pc = new ProxyCache(m, beanClass);
0760: pc.proxy = px;
0761: super .addWriteMethod(name, pc);
0762: } else {
0763: DynamicProxy px = createReflectionProxy(beanClass,
0764: name, stopClass);
0765: ProxyCache pc = new ProxyCache(m0, beanClass);
0766: pc.proxy = px;
0767: super .addReadMethod(name, pc);
0768: super .addWriteMethod(name, pc);
0769: }
0770: }
0771: }
0772:
0773: static void createMethodMap(ObjectDesc d, final Map readMethods,
0774: final Map writeMethods) {
0775: d.handleProperties(new PropertyHandler() {
0776: public void handle(String propertyName, Class type,
0777: Method readMethod, Method writeMethod) {
0778: if (readMethod != null) {
0779: readMethods.put(propertyName, readMethod);
0780: }
0781: if (writeMethod != null) {
0782: writeMethods.put(propertyName, writeMethod);
0783: }
0784: }
0785: });
0786: }
0787:
0788: static DynamicProxy createReflectionProxy(Class cls, String name,
0789: Class stopClass) {
0790: ObjectDesc od = ObjectDescFactory.getDefault().create(cls,
0791: stopClass);
0792: Map rmap = new HashMap();
0793: Map wmap = new HashMap();
0794: createMethodMap(od, rmap, wmap);
0795: Method w = (Method) rmap.get(name);
0796: Method r = (Method) wmap.get(name);
0797: if (r != null) {
0798: r.setAccessible(true);
0799: }
0800: if (w != null) {
0801: w.setAccessible(true);
0802: }
0803: final Method readMethod = r;
0804: final Method writeMethod = w;
0805: DynamicProxy px = new DynamicProxy() {
0806: public Object invoke(Object target) {
0807: try {
0808: return readMethod.invoke(target, noarg);
0809: } catch (IllegalAccessException iae) {
0810: } catch (InvocationTargetException ite) {
0811: }
0812: return null;
0813: }
0814:
0815: public Object invoke(Object target, Object[] args) {
0816: try {
0817: writeMethod.invoke(target, args);
0818: } catch (IllegalAccessException iae) {
0819: } catch (InvocationTargetException ite) {
0820: }
0821: return null;
0822: }
0823: };
0824: return px;
0825: }
0826:
0827: /**
0828: * Gets a Bean property of the specified bean.
0829: *
0830: * @param target the target bean
0831: * @param name the Bean property name
0832: */
0833: public Object getBeanProperty(Object target, String name)
0834: throws IllegalAccessException {
0835: return getBeanProperty(target, name, null);
0836: }
0837:
0838: /**
0839: * Gets a Bean property of the specified bean.
0840: *
0841: * @param target the target bean
0842: * @param name the Bean property name
0843: * @param stopClass the Introspector's "stopClass"
0844: */
0845: protected Object getBeanProperty(Object target, String name,
0846: Class stopClass) throws IllegalAccessException {
0847: pnuts.lang.Runtime.Accessor a = getAccessor(target.getClass(),
0848: stopClass);
0849: ProxyCache readMethodProxy = (ProxyCache) a
0850: .findReadMethod(name);
0851: if (readMethodProxy != null) {
0852: return readMethodProxy.invoke(target);
0853: }
0854: if (target instanceof Class) {
0855: Class cls = (Class) target;
0856: Field field = getField(cls, name);
0857: if (field != null
0858: && Modifier.isStatic(field.getModifiers())) {
0859: return field.get(null);
0860: }
0861: }
0862: if (a.findWriteMethod(name) == null) {
0863: Class cls = target.getClass();
0864: Field field = getField(cls, name);
0865: if (field != null
0866: && !Modifier.isStatic(field.getModifiers())) {
0867: return field.get(target);
0868: }
0869: }
0870: throw new IllegalArgumentException(
0871: "not readable property: target=" + target
0872: + ", fieldName=" + name);
0873: }
0874:
0875: /**
0876: * Sets a Bean property of the specified bean.
0877: *
0878: * @param target the target bean
0879: * @param name the Bean property name
0880: * @param value the new property value
0881: */
0882: public void setBeanProperty(Object target, String name, Object value)
0883: throws IllegalAccessException, InvocationTargetException {
0884: setBeanProperty(target, name, value, null);
0885: }
0886:
0887: /**
0888: * Sets a Bean property of the specified bean.
0889: *
0890: * @param target the target bean
0891: * @param name the Bean property name
0892: * @param value the new property value
0893: * @param stopClass the Introspector's "stopClass"
0894: */
0895: protected void setBeanProperty(Object target, String name,
0896: Object value, Class stopClass)
0897: throws IllegalAccessException, InvocationTargetException {
0898: pnuts.lang.Runtime.Accessor a = getAccessor(target.getClass(),
0899: stopClass);
0900: ProxyCache writeMethodProxy = (ProxyCache) a
0901: .findWriteMethod(name);
0902: if (writeMethodProxy != null) {
0903: try {
0904: Object[] arg = new Object[] { value };
0905: writeMethodProxy.invoke(target, arg);
0906: return;
0907: } catch (ClassCastException cce) {
0908: try {
0909: super .setBeanProperty(target, name, value,
0910: stopClass);
0911: return;
0912: } catch (Exception e) {
0913: Class type = getBeanPropertyType(target.getClass(),
0914: name);
0915:
0916: String msg = getMessage("pnuts.lang.pnuts",
0917: "type.mismatch", new Object[] { type,
0918: target, name, value,
0919: value.getClass().getName() });
0920: throw new IllegalArgumentException(msg);
0921: }
0922: }
0923: }
0924: if (target instanceof Class) {
0925: Class cls = (Class) target;
0926: Field field = getField(cls, name);
0927: if (field != null
0928: && Modifier.isStatic(field.getModifiers())) {
0929: field.set(null, value);
0930: return;
0931: }
0932: }
0933: if (a.findReadMethod(name) == null) {
0934: Class cls = target.getClass();
0935: Field field = getField(cls, name);
0936: if (field != null
0937: && !Modifier.isStatic(field.getModifiers())) {
0938: field.set(target, value);
0939: return;
0940: }
0941: }
0942: throw new IllegalArgumentException(
0943: "not writable property: target=" + target
0944: + ", fieldName=" + name);
0945: }
0946:
0947: /**
0948: * Gets the type of a bean property
0949: *
0950: * @param cls the class of the bean
0951: * @param name the property name of the bean property
0952: * @return the type of the property
0953: */
0954: public Class getBeanPropertyType(Class cls, String name) {
0955: return getAccessor(cls, null).getType(name);
0956: }
0957:
0958: protected Object _getField(Context context, Class c, String name,
0959: Object target) {
0960: try {
0961: ClassLoader cl = codeLoader;
0962: synchronized (this ) {
0963: if (cl == null) {
0964: cl = Compiler.createCodeLoader(context
0965: .getClassLoader(), true);
0966: codeLoader = (CodeLoader) cl;
0967: }
0968: }
0969: return _getField(context, c, name, target, (CodeLoader) cl);
0970: } catch (NoSuchFieldException nsf) {
0971: throw new PnutsException("field.notFound", new Object[] {
0972: name, target }, context);
0973: } catch (Exception e) {
0974: throw new PnutsException(e, context);
0975: }
0976: }
0977:
0978: protected void _putField(Context context, Class c, String name,
0979: Object target, Object value) {
0980: try {
0981: ClassLoader cl = codeLoader;
0982: synchronized (this ) {
0983: if (cl == null) {
0984: cl = Compiler.createCodeLoader(context
0985: .getClassLoader(), true);
0986: codeLoader = (CodeLoader) cl;
0987: }
0988: }
0989: _putField(context, c, name, target, value, (CodeLoader) cl);
0990: } catch (Exception e) {
0991: throw new PnutsException(e, context);
0992: }
0993: }
0994:
0995: Object _getField(Context context, Class c, String name,
0996: Object target, CodeLoader codeLoader)
0997: throws NoSuchFieldException, InstantiationException,
0998: IOException, IllegalAccessException {
0999: FieldAccessor fa = (FieldAccessor) fieldCache.get(c);
1000: if (fa == null) {
1001: if (target instanceof Class) {
1002: fa = FieldAccessorGenerator.generate(name,
1003: (Class) target, codeLoader, true);
1004: } else {
1005: fa = FieldAccessorGenerator.generate(name, c,
1006: codeLoader, false);
1007: }
1008: fieldCache.put(c, fa);
1009: }
1010: return fa.get(target);
1011: }
1012:
1013: void _putField(Context context, Class c, String name,
1014: Object target, Object value, CodeLoader codeLoader)
1015: throws NoSuchFieldException, InstantiationException,
1016: IOException, IllegalAccessException {
1017: FieldAccessor fa = (FieldAccessor) fieldCache.get(c);
1018: if (fa == null) {
1019: if (target instanceof Class) {
1020: fa = FieldAccessorGenerator.generate(name,
1021: (Class) target, codeLoader, true);
1022: } else {
1023: fa = FieldAccessorGenerator.generate(name, c,
1024: codeLoader, false);
1025: }
1026: fieldCache.put(c, fa);
1027: }
1028: fa.set(target, value);
1029: }
1030: }
|