001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
002: *
003: * ***** BEGIN LICENSE BLOCK *****
004: * Version: MPL 1.1/GPL 2.0
005: *
006: * The contents of this file are subject to the Mozilla Public License Version
007: * 1.1 (the "License"); you may not use this file except in compliance with
008: * the License. You may obtain a copy of the License at
009: * http://www.mozilla.org/MPL/
010: *
011: * Software distributed under the License is distributed on an "AS IS" basis,
012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
013: * for the specific language governing rights and limitations under the
014: * License.
015: *
016: * The Original Code is Rhino code, released
017: * May 6, 1999.
018: *
019: * The Initial Developer of the Original Code is
020: * Netscape Communications Corporation.
021: * Portions created by the Initial Developer are Copyright (C) 1997-2000
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Norris Boyd
026: * Cameron McCormack
027: * Frank Mitchell
028: * Mike Shaver
029: * Kurt Westerfeld
030: *
031: * Alternatively, the contents of this file may be used under the terms of
032: * the GNU General Public License Version 2 or later (the "GPL"), in which
033: * case the provisions of the GPL are applicable instead of those above. If
034: * you wish to allow use of your version of this file only under the terms of
035: * the GPL and not to allow others to use your version of this file under the
036: * MPL, indicate your decision by deleting the provisions above and replacing
037: * them with the notice and other provisions required by the GPL. If you do
038: * not delete the provisions above, a recipient may use your version of this
039: * file under either the MPL or the GPL.
040: *
041: * ***** END LICENSE BLOCK ***** */
042:
043: package org.mozilla.javascript;
044:
045: import java.lang.reflect.*;
046: import java.util.*;
047:
048: /**
049: *
050: * @author Mike Shaver
051: * @author Norris Boyd
052: * @see NativeJavaObject
053: * @see NativeJavaClass
054: */
055: class JavaMembers {
056: JavaMembers(Scriptable scope, Class cl) {
057: this (scope, cl, false);
058: }
059:
060: JavaMembers(Scriptable scope, Class cl, boolean includeProtected) {
061: try {
062: Context cx = ContextFactory.getGlobal().enterContext();
063: ClassShutter shutter = cx.getClassShutter();
064: if (shutter != null
065: && !shutter.visibleToScripts(cl.getName())) {
066: throw Context.reportRuntimeError1(
067: "msg.access.prohibited", cl.getName());
068: }
069: this .includePrivate = cx
070: .hasFeature(Context.FEATURE_ENHANCED_JAVA_ACCESS);
071: this .members = new Hashtable(23);
072: this .staticMembers = new Hashtable(7);
073: this .cl = cl;
074: reflect(scope, includeProtected);
075: } finally {
076: Context.exit();
077: }
078: }
079:
080: boolean has(String name, boolean isStatic) {
081: Hashtable ht = isStatic ? staticMembers : members;
082: Object obj = ht.get(name);
083: if (obj != null) {
084: return true;
085: }
086: return findExplicitFunction(name, isStatic) != null;
087: }
088:
089: Object get(Scriptable scope, String name, Object javaObject,
090: boolean isStatic) {
091: Hashtable ht = isStatic ? staticMembers : members;
092: Object member = ht.get(name);
093: if (!isStatic && member == null) {
094: // Try to get static member from instance (LC3)
095: member = staticMembers.get(name);
096: }
097: if (member == null) {
098: member = this .getExplicitFunction(scope, name, javaObject,
099: isStatic);
100: if (member == null)
101: return Scriptable.NOT_FOUND;
102: }
103: if (member instanceof Scriptable) {
104: return member;
105: }
106: Context cx = Context.getContext();
107: Object rval;
108: Class type;
109: try {
110: if (member instanceof BeanProperty) {
111: BeanProperty bp = (BeanProperty) member;
112: if (bp.getter == null)
113: return Scriptable.NOT_FOUND;
114: rval = bp.getter.invoke(javaObject, Context.emptyArgs);
115: type = bp.getter.method().getReturnType();
116: } else {
117: Field field = (Field) member;
118: rval = field.get(isStatic ? null : javaObject);
119: type = field.getType();
120: }
121: } catch (Exception ex) {
122: throw Context.throwAsScriptRuntimeEx(ex);
123: }
124: // Need to wrap the object before we return it.
125: scope = ScriptableObject.getTopLevelScope(scope);
126: return cx.getWrapFactory().wrap(cx, scope, rval, type);
127: }
128:
129: void put(Scriptable scope, String name, Object javaObject,
130: Object value, boolean isStatic) {
131: Hashtable ht = isStatic ? staticMembers : members;
132: Object member = ht.get(name);
133: if (!isStatic && member == null) {
134: // Try to get static member from instance (LC3)
135: member = staticMembers.get(name);
136: }
137: if (member == null)
138: throw reportMemberNotFound(name);
139: if (member instanceof FieldAndMethods) {
140: FieldAndMethods fam = (FieldAndMethods) ht.get(name);
141: member = fam.field;
142: }
143:
144: // Is this a bean property "set"?
145: if (member instanceof BeanProperty) {
146: BeanProperty bp = (BeanProperty) member;
147: if (bp.setter == null) {
148: throw reportMemberNotFound(name);
149: }
150: // If there's only one setter or if the value is null, use the
151: // main setter. Otherwise, let the NativeJavaMethod decide which
152: // setter to use:
153: if (bp.setters == null || value == null) {
154: Class setType = bp.setter.argTypes[0];
155: Object[] args = { Context.jsToJava(value, setType) };
156: try {
157: bp.setter.invoke(javaObject, args);
158: } catch (Exception ex) {
159: throw Context.throwAsScriptRuntimeEx(ex);
160: }
161: } else {
162: Object[] args = { value };
163: bp.setters.call(Context.getContext(), ScriptableObject
164: .getTopLevelScope(scope), scope, args);
165: }
166: } else {
167: if (!(member instanceof Field)) {
168: String str = (member == null) ? "msg.java.internal.private"
169: : "msg.java.method.assign";
170: throw Context.reportRuntimeError1(str, name);
171: }
172: Field field = (Field) member;
173: Object javaValue = Context.jsToJava(value, field.getType());
174: try {
175: field.set(javaObject, javaValue);
176: } catch (IllegalAccessException accessEx) {
177: if ((field.getModifiers() & Modifier.FINAL) != 0) {
178: // treat Java final the same as JavaScript [[READONLY]]
179: return;
180: }
181: throw Context.throwAsScriptRuntimeEx(accessEx);
182: } catch (IllegalArgumentException argEx) {
183: throw Context.reportRuntimeError3(
184: "msg.java.internal.field.type", value
185: .getClass().getName(), field,
186: javaObject.getClass().getName());
187: }
188: }
189: }
190:
191: Object[] getIds(boolean isStatic) {
192: Hashtable ht = isStatic ? staticMembers : members;
193: int len = ht.size();
194: Object[] result = new Object[len];
195: Enumeration keys = ht.keys();
196: for (int i = 0; i < len; i++)
197: result[i] = keys.nextElement();
198: return result;
199: }
200:
201: static String javaSignature(Class type) {
202: if (!type.isArray()) {
203: return type.getName();
204: } else {
205: int arrayDimension = 0;
206: do {
207: ++arrayDimension;
208: type = type.getComponentType();
209: } while (type.isArray());
210: String name = type.getName();
211: String suffix = "[]";
212: if (arrayDimension == 1) {
213: return name.concat(suffix);
214: } else {
215: int length = name.length() + arrayDimension
216: * suffix.length();
217: StringBuffer sb = new StringBuffer(length);
218: sb.append(name);
219: while (arrayDimension != 0) {
220: --arrayDimension;
221: sb.append(suffix);
222: }
223: return sb.toString();
224: }
225: }
226: }
227:
228: static String liveConnectSignature(Class[] argTypes) {
229: int N = argTypes.length;
230: if (N == 0) {
231: return "()";
232: }
233: StringBuffer sb = new StringBuffer();
234: sb.append('(');
235: for (int i = 0; i != N; ++i) {
236: if (i != 0) {
237: sb.append(',');
238: }
239: sb.append(javaSignature(argTypes[i]));
240: }
241: sb.append(')');
242: return sb.toString();
243: }
244:
245: private MemberBox findExplicitFunction(String name, boolean isStatic) {
246: int sigStart = name.indexOf('(');
247: if (sigStart < 0) {
248: return null;
249: }
250:
251: Hashtable ht = isStatic ? staticMembers : members;
252: MemberBox[] methodsOrCtors = null;
253: boolean isCtor = (isStatic && sigStart == 0);
254:
255: if (isCtor) {
256: // Explicit request for an overloaded constructor
257: methodsOrCtors = ctors;
258: } else {
259: // Explicit request for an overloaded method
260: String trueName = name.substring(0, sigStart);
261: Object obj = ht.get(trueName);
262: if (!isStatic && obj == null) {
263: // Try to get static member from instance (LC3)
264: obj = staticMembers.get(trueName);
265: }
266: if (obj instanceof NativeJavaMethod) {
267: NativeJavaMethod njm = (NativeJavaMethod) obj;
268: methodsOrCtors = njm.methods;
269: }
270: }
271:
272: if (methodsOrCtors != null) {
273: for (int i = 0; i < methodsOrCtors.length; i++) {
274: Class[] type = methodsOrCtors[i].argTypes;
275: String sig = liveConnectSignature(type);
276: if (sigStart + sig.length() == name.length()
277: && name.regionMatches(sigStart, sig, 0, sig
278: .length())) {
279: return methodsOrCtors[i];
280: }
281: }
282: }
283:
284: return null;
285: }
286:
287: private Object getExplicitFunction(Scriptable scope, String name,
288: Object javaObject, boolean isStatic) {
289: Hashtable ht = isStatic ? staticMembers : members;
290: Object member = null;
291: MemberBox methodOrCtor = findExplicitFunction(name, isStatic);
292:
293: if (methodOrCtor != null) {
294: Scriptable prototype = ScriptableObject
295: .getFunctionPrototype(scope);
296:
297: if (methodOrCtor.isCtor()) {
298: NativeJavaConstructor fun = new NativeJavaConstructor(
299: methodOrCtor);
300: fun.setPrototype(prototype);
301: member = fun;
302: ht.put(name, fun);
303: } else {
304: String trueName = methodOrCtor.getName();
305: member = ht.get(trueName);
306:
307: if (member instanceof NativeJavaMethod
308: && ((NativeJavaMethod) member).methods.length > 1) {
309: NativeJavaMethod fun = new NativeJavaMethod(
310: methodOrCtor, name);
311: fun.setPrototype(prototype);
312: ht.put(name, fun);
313: member = fun;
314: }
315: }
316: }
317:
318: return member;
319: }
320:
321: /**
322: * Retrieves mapping of methods to accessible methods for a class.
323: * In case the class is not public, retrieves methods with same
324: * signature as its public methods from public superclasses and
325: * interfaces (if they exist). Basically upcasts every method to the
326: * nearest accessible method.
327: */
328: private static Method[] discoverAccessibleMethods(Class clazz,
329: boolean includeProtected, boolean includePrivate) {
330: Map map = new HashMap();
331: discoverAccessibleMethods(clazz, map, includeProtected,
332: includePrivate);
333: return (Method[]) map.values().toArray(new Method[map.size()]);
334: }
335:
336: private static void discoverAccessibleMethods(Class clazz, Map map,
337: boolean includeProtected, boolean includePrivate) {
338: if (Modifier.isPublic(clazz.getModifiers()) || includePrivate) {
339: try {
340: if (includeProtected || includePrivate) {
341: while (clazz != null) {
342: try {
343: Method[] methods = clazz
344: .getDeclaredMethods();
345: for (int i = 0; i < methods.length; i++) {
346: Method method = methods[i];
347: int mods = method.getModifiers();
348:
349: if (Modifier.isPublic(mods)
350: || Modifier.isProtected(mods)
351: || includePrivate) {
352: if (includePrivate)
353: method.setAccessible(true);
354: map
355: .put(new MethodSignature(
356: method), method);
357: }
358: }
359: clazz = clazz.getSuperclass();
360: } catch (SecurityException e) {
361: // Some security settings (i.e., applets) disallow
362: // access to Class.getDeclaredMethods. Fall back to
363: // Class.getMethods.
364: Method[] methods = clazz.getMethods();
365: for (int i = 0; i < methods.length; i++) {
366: Method method = methods[i];
367: MethodSignature sig = new MethodSignature(
368: method);
369: if (map.get(sig) == null)
370: map.put(sig, method);
371: }
372: break; // getMethods gets superclass methods, no
373: // need to loop any more
374: }
375: }
376: } else {
377: Method[] methods = clazz.getMethods();
378: for (int i = 0; i < methods.length; i++) {
379: Method method = methods[i];
380: MethodSignature sig = new MethodSignature(
381: method);
382: map.put(sig, method);
383: }
384: }
385: return;
386: } catch (SecurityException e) {
387: Context
388: .reportWarning("Could not discover accessible methods of class "
389: + clazz.getName()
390: + " due to lack of privileges, "
391: + "attemping superclasses/interfaces.");
392: // Fall through and attempt to discover superclass/interface
393: // methods
394: }
395: }
396:
397: Class[] interfaces = clazz.getInterfaces();
398: for (int i = 0; i < interfaces.length; i++) {
399: discoverAccessibleMethods(interfaces[i], map,
400: includeProtected, includePrivate);
401: }
402: Class super class = clazz.getSuperclass();
403: if (super class != null) {
404: discoverAccessibleMethods(super class, map,
405: includeProtected, includePrivate);
406: }
407: }
408:
409: private static final class MethodSignature {
410: private final String name;
411: private final Class[] args;
412:
413: private MethodSignature(String name, Class[] args) {
414: this .name = name;
415: this .args = args;
416: }
417:
418: MethodSignature(Method method) {
419: this (method.getName(), method.getParameterTypes());
420: }
421:
422: public boolean equals(Object o) {
423: if (o instanceof MethodSignature) {
424: MethodSignature ms = (MethodSignature) o;
425: return ms.name.equals(name)
426: && Arrays.equals(args, ms.args);
427: }
428: return false;
429: }
430:
431: public int hashCode() {
432: return name.hashCode() ^ args.length;
433: }
434: }
435:
436: private void reflect(Scriptable scope, boolean includeProtected) {
437: // We reflect methods first, because we want overloaded field/method
438: // names to be allocated to the NativeJavaMethod before the field
439: // gets in the way.
440:
441: Method[] methods = discoverAccessibleMethods(cl,
442: includeProtected, includePrivate);
443: for (int i = 0; i < methods.length; i++) {
444: Method method = methods[i];
445: int mods = method.getModifiers();
446: boolean isStatic = Modifier.isStatic(mods);
447: Hashtable ht = isStatic ? staticMembers : members;
448: String name = method.getName();
449: Object value = ht.get(name);
450: if (value == null) {
451: ht.put(name, method);
452: } else {
453: ObjArray overloadedMethods;
454: if (value instanceof ObjArray) {
455: overloadedMethods = (ObjArray) value;
456: } else {
457: if (!(value instanceof Method))
458: Kit.codeBug();
459: // value should be instance of Method as at this stage
460: // staticMembers and members can only contain methods
461: overloadedMethods = new ObjArray();
462: overloadedMethods.add(value);
463: ht.put(name, overloadedMethods);
464: }
465: overloadedMethods.add(method);
466: }
467: }
468:
469: // replace Method instances by wrapped NativeJavaMethod objects
470: // first in staticMembers and then in members
471: for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
472: boolean isStatic = (tableCursor == 0);
473: Hashtable ht = (isStatic) ? staticMembers : members;
474: Enumeration e = ht.keys();
475: while (e.hasMoreElements()) {
476: String name = (String) e.nextElement();
477: MemberBox[] methodBoxes;
478: Object value = ht.get(name);
479: if (value instanceof Method) {
480: methodBoxes = new MemberBox[1];
481: methodBoxes[0] = new MemberBox((Method) value);
482: } else {
483: ObjArray overloadedMethods = (ObjArray) value;
484: int N = overloadedMethods.size();
485: if (N < 2)
486: Kit.codeBug();
487: methodBoxes = new MemberBox[N];
488: for (int i = 0; i != N; ++i) {
489: Method method = (Method) overloadedMethods
490: .get(i);
491: methodBoxes[i] = new MemberBox(method);
492: }
493: }
494: NativeJavaMethod fun = new NativeJavaMethod(methodBoxes);
495: if (scope != null) {
496: ScriptRuntime.setFunctionProtoAndParent(fun, scope);
497: }
498: ht.put(name, fun);
499: }
500: }
501:
502: // Reflect fields.
503: Field[] fields = getAccessibleFields();
504: for (int i = 0; i < fields.length; i++) {
505: Field field = fields[i];
506: String name = field.getName();
507: int mods = field.getModifiers();
508: if (!includePrivate && !Modifier.isPublic(mods)) {
509: continue;
510: }
511: try {
512: boolean isStatic = Modifier.isStatic(mods);
513: Hashtable ht = isStatic ? staticMembers : members;
514: Object member = ht.get(name);
515: if (member == null) {
516: ht.put(name, field);
517: } else if (member instanceof NativeJavaMethod) {
518: NativeJavaMethod method = (NativeJavaMethod) member;
519: FieldAndMethods fam = new FieldAndMethods(scope,
520: method.methods, field);
521: Hashtable fmht = isStatic ? staticFieldAndMethods
522: : fieldAndMethods;
523: if (fmht == null) {
524: fmht = new Hashtable(4);
525: if (isStatic) {
526: staticFieldAndMethods = fmht;
527: } else {
528: fieldAndMethods = fmht;
529: }
530: }
531: fmht.put(name, fam);
532: ht.put(name, fam);
533: } else if (member instanceof Field) {
534: Field oldField = (Field) member;
535: // If this newly reflected field shadows an inherited field,
536: // then replace it. Otherwise, since access to the field
537: // would be ambiguous from Java, no field should be
538: // reflected.
539: // For now, the first field found wins, unless another field
540: // explicitly shadows it.
541: if (oldField.getDeclaringClass().isAssignableFrom(
542: field.getDeclaringClass())) {
543: ht.put(name, field);
544: }
545: } else {
546: // "unknown member type"
547: Kit.codeBug();
548: }
549: } catch (SecurityException e) {
550: // skip this field
551: Context.reportWarning("Could not access field " + name
552: + " of class " + cl.getName()
553: + " due to lack of privileges.");
554: }
555: }
556:
557: // Create bean propeties from corresponding get/set methods first for
558: // static members and then for instance members
559: for (int tableCursor = 0; tableCursor != 2; ++tableCursor) {
560: boolean isStatic = (tableCursor == 0);
561: Hashtable ht = (isStatic) ? staticMembers : members;
562:
563: Hashtable toAdd = new Hashtable();
564:
565: // Now, For each member, make "bean" properties.
566: for (Enumeration e = ht.keys(); e.hasMoreElements();) {
567:
568: // Is this a getter?
569: String name = (String) e.nextElement();
570: boolean memberIsGetMethod = name.startsWith("get");
571: boolean memberIsSetMethod = name.startsWith("set");
572: boolean memberIsIsMethod = name.startsWith("is");
573: if (memberIsGetMethod || memberIsIsMethod
574: || memberIsSetMethod) {
575: // Double check name component.
576: String nameComponent = name
577: .substring(memberIsIsMethod ? 2 : 3);
578: if (nameComponent.length() == 0)
579: continue;
580:
581: // Make the bean property name.
582: String beanPropertyName = nameComponent;
583: char ch0 = nameComponent.charAt(0);
584: if (Character.isUpperCase(ch0)) {
585: if (nameComponent.length() == 1) {
586: beanPropertyName = nameComponent
587: .toLowerCase();
588: } else {
589: char ch1 = nameComponent.charAt(1);
590: if (!Character.isUpperCase(ch1)) {
591: beanPropertyName = Character
592: .toLowerCase(ch0)
593: + nameComponent.substring(1);
594: }
595: }
596: }
597:
598: // If we already have a member by this name, don't do this
599: // property.
600: if (ht.containsKey(beanPropertyName)
601: || toAdd.containsKey(beanPropertyName)) {
602: continue;
603: }
604:
605: // Find the getter method, or if there is none, the is-
606: // method.
607: MemberBox getter = null;
608: getter = findGetter(isStatic, ht, "get",
609: nameComponent);
610: // If there was no valid getter, check for an is- method.
611: if (getter == null) {
612: getter = findGetter(isStatic, ht, "is",
613: nameComponent);
614: }
615:
616: // setter
617: MemberBox setter = null;
618: NativeJavaMethod setters = null;
619: String setterName = "set".concat(nameComponent);
620:
621: if (ht.containsKey(setterName)) {
622: // Is this value a method?
623: Object member = ht.get(setterName);
624: if (member instanceof NativeJavaMethod) {
625: NativeJavaMethod njmSet = (NativeJavaMethod) member;
626: if (getter != null) {
627: // We have a getter. Now, do we have a matching
628: // setter?
629: Class type = getter.method()
630: .getReturnType();
631: setter = extractSetMethod(type,
632: njmSet.methods, isStatic);
633: } else {
634: // No getter, find any set method
635: setter = extractSetMethod(
636: njmSet.methods, isStatic);
637: }
638: if (njmSet.methods.length > 1) {
639: setters = njmSet;
640: }
641: }
642: }
643: // Make the property.
644: BeanProperty bp = new BeanProperty(getter, setter,
645: setters);
646: toAdd.put(beanPropertyName, bp);
647: }
648: }
649:
650: // Add the new bean properties.
651: for (Enumeration e = toAdd.keys(); e.hasMoreElements();) {
652: Object key = e.nextElement();
653: Object value = toAdd.get(key);
654: ht.put(key, value);
655: }
656: }
657:
658: // Reflect constructors
659: Constructor[] constructors = getAccessibleConstructors();
660: ctors = new MemberBox[constructors.length];
661: for (int i = 0; i != constructors.length; ++i) {
662: ctors[i] = new MemberBox(constructors[i]);
663: }
664: }
665:
666: private Constructor[] getAccessibleConstructors() {
667: // The JVM currently doesn't allow changing access on java.lang.Class
668: // constructors, so don't try
669: if (includePrivate && cl != ScriptRuntime.ClassClass) {
670: try {
671: Constructor[] cons = cl.getDeclaredConstructors();
672: Constructor.setAccessible(cons, true);
673:
674: return cons;
675: } catch (SecurityException e) {
676: // Fall through to !includePrivate case
677: Context.reportWarning("Could not access constructor "
678: + " of class " + cl.getName()
679: + " due to lack of privileges.");
680: }
681: }
682: return cl.getConstructors();
683: }
684:
685: private Field[] getAccessibleFields() {
686: if (includePrivate) {
687: try {
688: ArrayList fieldsList = new ArrayList();
689: Class currentClass = cl;
690:
691: while (currentClass != null) {
692: // get all declared fields in this class, make them
693: // accessible, and save
694: Field[] declared = currentClass.getDeclaredFields();
695: for (int i = 0; i < declared.length; i++) {
696: declared[i].setAccessible(true);
697: fieldsList.add(declared[i]);
698: }
699: // walk up superclass chain. no need to deal specially with
700: // interfaces, since they can't have fields
701: currentClass = currentClass.getSuperclass();
702: }
703:
704: return (Field[]) fieldsList
705: .toArray(new Field[fieldsList.size()]);
706: } catch (SecurityException e) {
707: // fall through to !includePrivate case
708: }
709: }
710: return cl.getFields();
711: }
712:
713: private MemberBox findGetter(boolean isStatic, Hashtable ht,
714: String prefix, String propertyName) {
715: String getterName = prefix.concat(propertyName);
716: if (ht.containsKey(getterName)) {
717: // Check that the getter is a method.
718: Object member = ht.get(getterName);
719: if (member instanceof NativeJavaMethod) {
720: NativeJavaMethod njmGet = (NativeJavaMethod) member;
721: return extractGetMethod(njmGet.methods, isStatic);
722: }
723: }
724: return null;
725: }
726:
727: private static MemberBox extractGetMethod(MemberBox[] methods,
728: boolean isStatic) {
729: // Inspect the list of all MemberBox for the only one having no
730: // parameters
731: for (int methodIdx = 0; methodIdx < methods.length; methodIdx++) {
732: MemberBox method = methods[methodIdx];
733: // Does getter method have an empty parameter list with a return
734: // value (eg. a getSomething() or isSomething())?
735: if (method.argTypes.length == 0
736: && (!isStatic || method.isStatic())) {
737: Class type = method.method().getReturnType();
738: if (type != Void.TYPE) {
739: return method;
740: }
741: break;
742: }
743: }
744: return null;
745: }
746:
747: private static MemberBox extractSetMethod(Class type,
748: MemberBox[] methods, boolean isStatic) {
749: //
750: // Note: it may be preferable to allow NativeJavaMethod.findFunction()
751: // to find the appropriate setter; unfortunately, it requires an
752: // instance of the target arg to determine that.
753: //
754:
755: // Make two passes: one to find a method with direct type assignment,
756: // and one to find a widening conversion.
757: for (int pass = 1; pass <= 2; ++pass) {
758: for (int i = 0; i < methods.length; ++i) {
759: MemberBox method = methods[i];
760: if (!isStatic || method.isStatic()) {
761: Class[] params = method.argTypes;
762: if (params.length == 1) {
763: if (pass == 1) {
764: if (params[0] == type) {
765: return method;
766: }
767: } else {
768: if (pass != 2)
769: Kit.codeBug();
770: if (params[0].isAssignableFrom(type)) {
771: return method;
772: }
773: }
774: }
775: }
776: }
777: }
778: return null;
779: }
780:
781: private static MemberBox extractSetMethod(MemberBox[] methods,
782: boolean isStatic) {
783:
784: for (int i = 0; i < methods.length; ++i) {
785: MemberBox method = methods[i];
786: if (!isStatic || method.isStatic()) {
787: if (method.method().getReturnType() == Void.TYPE) {
788: if (method.argTypes.length == 1) {
789: return method;
790: }
791: }
792: }
793: }
794: return null;
795: }
796:
797: Hashtable getFieldAndMethodsObjects(Scriptable scope,
798: Object javaObject, boolean isStatic) {
799: Hashtable ht = isStatic ? staticFieldAndMethods
800: : fieldAndMethods;
801: if (ht == null)
802: return null;
803: int len = ht.size();
804: Hashtable result = new Hashtable(len);
805: Enumeration e = ht.elements();
806: while (len-- > 0) {
807: FieldAndMethods fam = (FieldAndMethods) e.nextElement();
808: FieldAndMethods famNew = new FieldAndMethods(scope,
809: fam.methods, fam.field);
810: famNew.javaObject = javaObject;
811: result.put(fam.field.getName(), famNew);
812: }
813: return result;
814: }
815:
816: static JavaMembers lookupClass(Scriptable scope, Class dynamicType,
817: Class staticType, boolean includeProtected) {
818: JavaMembers members;
819: scope = ScriptableObject.getTopLevelScope(scope);
820: ClassCache cache = ClassCache.get(scope);
821: Map<Class<?>, JavaMembers> ct = cache.getClassCacheMap();
822:
823: Class cl = dynamicType;
824: for (;;) {
825: members = ct.get(cl);
826: if (members != null) {
827: return members;
828: }
829: try {
830: members = new JavaMembers(scope, cl, includeProtected);
831: break;
832: } catch (SecurityException e) {
833: // Reflection may fail for objects that are in a restricted
834: // access package (e.g. sun.*). If we get a security
835: // exception, try again with the static type if it is interface.
836: // Otherwise, try superclass
837: if (staticType != null && staticType.isInterface()) {
838: cl = staticType;
839: staticType = null; // try staticType only once
840: } else {
841: Class parent = cl.getSuperclass();
842: if (parent == null) {
843: if (cl.isInterface()) {
844: // last resort after failed staticType interface
845: parent = ScriptRuntime.ObjectClass;
846: } else {
847: throw e;
848: }
849: }
850: cl = parent;
851: }
852: }
853: }
854:
855: if (cache.isCachingEnabled())
856: ct.put(cl, members);
857: return members;
858: }
859:
860: RuntimeException reportMemberNotFound(String memberName) {
861: return Context.reportRuntimeError2("msg.java.member.not.found",
862: cl.getName(), memberName);
863: }
864:
865: private Class cl;
866: private Hashtable members;
867: private Hashtable fieldAndMethods;
868: private Hashtable staticMembers;
869: private Hashtable staticFieldAndMethods;
870: MemberBox[] ctors;
871: private boolean includePrivate;
872: }
873:
874: class BeanProperty {
875: BeanProperty(MemberBox getter, MemberBox setter,
876: NativeJavaMethod setters) {
877: this .getter = getter;
878: this .setter = setter;
879: this .setters = setters;
880: }
881:
882: MemberBox getter;
883: MemberBox setter;
884: NativeJavaMethod setters;
885: }
886:
887: class FieldAndMethods extends NativeJavaMethod {
888: static final long serialVersionUID = -9222428244284796755L;
889:
890: FieldAndMethods(Scriptable scope, MemberBox[] methods, Field field) {
891: super (methods);
892: this .field = field;
893: setParentScope(scope);
894: setPrototype(ScriptableObject.getFunctionPrototype(scope));
895: }
896:
897: public Object getDefaultValue(Class hint) {
898: if (hint == ScriptRuntime.FunctionClass)
899: return this ;
900: Object rval;
901: Class type;
902: try {
903: rval = field.get(javaObject);
904: type = field.getType();
905: } catch (IllegalAccessException accEx) {
906: throw Context.reportRuntimeError1(
907: "msg.java.internal.private", field.getName());
908: }
909: Context cx = Context.getContext();
910: rval = cx.getWrapFactory().wrap(cx, this , rval, type);
911: if (rval instanceof Scriptable) {
912: rval = ((Scriptable) rval).getDefaultValue(hint);
913: }
914: return rval;
915: }
916:
917: Field field;
918: Object javaObject;
919: }
|