+++ /dev/null
-/* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-\r
- *\r
- * The contents of this file are subject to the Netscape Public\r
- * License Version 1.1 (the "License"); you may not use this file\r
- * except in compliance with the License. You may obtain a copy of\r
- * the License at http://www.mozilla.org/NPL/\r
- *\r
- * Software distributed under the License is distributed on an "AS\r
- * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr\r
- * implied. See the License for the specific language governing\r
- * rights and limitations under the License.\r
- *\r
- * The Original Code is Rhino code, released\r
- * May 6, 1999.\r
- *\r
- * The Initial Developer of the Original Code is Netscape\r
- * Communications Corporation. Portions created by Netscape are\r
- * Copyright (C) 1997-2000 Netscape Communications Corporation. All\r
- * Rights Reserved.\r
- *\r
- * Contributor(s): \r
- * Norris Boyd\r
- * Frank Mitchell\r
- * Mike Shaver\r
- * Kurt Westerfeld\r
- *\r
- * Alternatively, the contents of this file may be used under the\r
- * terms of the GNU Public License (the "GPL"), in which case the\r
- * provisions of the GPL are applicable instead of those above.\r
- * If you wish to allow use of your version of this file only\r
- * under the terms of the GPL and not to allow others to use your\r
- * version of this file under the NPL, indicate your decision by\r
- * deleting the provisions above and replace them with the notice\r
- * and other provisions required by the GPL. If you do not delete\r
- * the provisions above, a recipient may use your version of this\r
- * file under either the NPL or the GPL.\r
- */\r
-\r
-package org.mozilla.javascript;\r
-\r
-import java.lang.reflect.*;\r
-import java.util.Hashtable;\r
-import java.util.Enumeration;\r
-\r
-/**\r
- *\r
- * @author Mike Shaver\r
- * @author Norris Boyd\r
- * @see NativeJavaObject\r
- * @see NativeJavaClass\r
- */\r
-class JavaMembers {\r
-\r
- JavaMembers(Scriptable scope, Class cl) {\r
- this.members = new Hashtable(23);\r
- this.staticMembers = new Hashtable(7);\r
- this.cl = cl;\r
- reflect(scope, cl);\r
- }\r
-\r
- boolean has(String name, boolean isStatic) {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- Object obj = ht.get(name);\r
- if (obj != null) {\r
- return true;\r
- } else {\r
- Member member = this.findExplicitFunction(name, isStatic);\r
- return member != null;\r
- }\r
- }\r
-\r
- Object get(Scriptable scope, String name, Object javaObject,\r
- boolean isStatic)\r
- {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- Object member = ht.get(name);\r
- if (!isStatic && member == null) {\r
- // Try to get static member from instance (LC3)\r
- member = staticMembers.get(name);\r
- }\r
- if (member == null) {\r
- member = this.getExplicitFunction(scope, name, \r
- javaObject, isStatic);\r
- if (member == null)\r
- return Scriptable.NOT_FOUND;\r
- }\r
- if (member instanceof Scriptable)\r
- return member; // why is this here?\r
- Object rval;\r
- Class type;\r
- try {\r
- if (member instanceof BeanProperty) {\r
- BeanProperty bp = (BeanProperty) member;\r
- rval = bp.getter.invoke(javaObject, ScriptRuntime.emptyArgs);\r
- type = bp.getter.getReturnType();\r
- } else {\r
- Field field = (Field) member;\r
- rval = field.get(isStatic ? null : javaObject);\r
- type = field.getType();\r
- }\r
- } catch (IllegalAccessException accEx) {\r
- throw new RuntimeException("unexpected IllegalAccessException "+\r
- "accessing Java field");\r
- } catch (InvocationTargetException e) {\r
- throw WrappedException.wrapException(\r
- JavaScriptException.wrapException(scope, e));\r
- }\r
- // Need to wrap the object before we return it.\r
- scope = ScriptableObject.getTopLevelScope(scope);\r
- return NativeJavaObject.wrap(scope, rval, type);\r
- }\r
-\r
- Member findExplicitFunction(String name, boolean isStatic) {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- int sigStart = name.indexOf('(');\r
- Member[] methodsOrCtors = null;\r
- NativeJavaMethod method = null;\r
- boolean isCtor = (isStatic && sigStart == 0);\r
-\r
- if (isCtor) {\r
- // Explicit request for an overloaded constructor\r
- methodsOrCtors = ctors;\r
- }\r
- else if (sigStart > 0) {\r
- // Explicit request for an overloaded method\r
- String trueName = name.substring(0,sigStart);\r
- Object obj = ht.get(trueName);\r
- if (!isStatic && obj == null) {\r
- // Try to get static member from instance (LC3)\r
- obj = staticMembers.get(trueName);\r
- }\r
- if (obj != null && obj instanceof NativeJavaMethod) {\r
- method = (NativeJavaMethod)obj;\r
- methodsOrCtors = method.getMethods();\r
- }\r
- }\r
-\r
- if (methodsOrCtors != null) {\r
- for (int i = 0; i < methodsOrCtors.length; i++) {\r
- String nameWithSig = \r
- NativeJavaMethod.signature(methodsOrCtors[i]);\r
- if (name.equals(nameWithSig)) {\r
- return methodsOrCtors[i];\r
- }\r
- }\r
- }\r
-\r
- return null;\r
- }\r
-\r
- Object getExplicitFunction(Scriptable scope, String name, \r
- Object javaObject, boolean isStatic) \r
- {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- Object member = null;\r
- Member methodOrCtor = this.findExplicitFunction(name, isStatic);\r
-\r
- if (methodOrCtor != null) {\r
- Scriptable prototype = \r
- ScriptableObject.getFunctionPrototype(scope);\r
-\r
- if (methodOrCtor instanceof Constructor) {\r
- NativeJavaConstructor fun = \r
- new NativeJavaConstructor((Constructor)methodOrCtor);\r
- fun.setPrototype(prototype);\r
- member = fun;\r
- ht.put(name, fun);\r
- } else {\r
- String trueName = methodOrCtor.getName();\r
- member = ht.get(trueName);\r
-\r
- if (member instanceof NativeJavaMethod &&\r
- ((NativeJavaMethod)member).getMethods().length > 1 ) {\r
- NativeJavaMethod fun = \r
- new NativeJavaMethod((Method)methodOrCtor, name);\r
- fun.setPrototype(prototype);\r
- ht.put(name, fun);\r
- member = fun;\r
- }\r
- }\r
- }\r
-\r
- return member;\r
- }\r
-\r
-\r
- public void put(Scriptable scope, String name, Object javaObject, \r
- Object value, boolean isStatic)\r
- {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- Object member = ht.get(name);\r
- if (!isStatic && member == null) {\r
- // Try to get static member from instance (LC3)\r
- member = staticMembers.get(name);\r
- }\r
- if (member == null)\r
- throw reportMemberNotFound(name);\r
- if (member instanceof FieldAndMethods) {\r
- FieldAndMethods fam = (FieldAndMethods) ht.get(name);\r
- member = fam.getField();\r
- }\r
- \r
- // Is this a bean property "set"?\r
- if (member instanceof BeanProperty) { \r
- try {\r
- Method method = ((BeanProperty) member).setter;\r
- if (method == null)\r
- throw reportMemberNotFound(name);\r
- Class[] types = method.getParameterTypes();\r
- Object[] params = { NativeJavaObject.coerceType(types[0], value) };\r
- method.invoke(javaObject, params);\r
- } catch (IllegalAccessException accessEx) {\r
- throw new RuntimeException("unexpected IllegalAccessException " +\r
- "accessing Java field");\r
- } catch (InvocationTargetException e) {\r
- throw WrappedException.wrapException(\r
- JavaScriptException.wrapException(scope, e));\r
- }\r
- }\r
- else {\r
- Field field = null;\r
- try {\r
- field = (Field) member;\r
- if (field == null) {\r
- throw Context.reportRuntimeError1(\r
- "msg.java.internal.private", name);\r
- }\r
- field.set(javaObject,\r
- NativeJavaObject.coerceType(field.getType(), value));\r
- } catch (ClassCastException e) {\r
- throw Context.reportRuntimeError1(\r
- "msg.java.method.assign", name);\r
- } catch (IllegalAccessException accessEx) {\r
- throw new RuntimeException("unexpected IllegalAccessException "+\r
- "accessing Java field");\r
- } catch (IllegalArgumentException argEx) {\r
- throw Context.reportRuntimeError3(\r
- "msg.java.internal.field.type", \r
- value.getClass().getName(), field,\r
- javaObject.getClass().getName());\r
- }\r
- }\r
- }\r
-\r
- Object[] getIds(boolean isStatic) {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- int len = ht.size();\r
- Object[] result = new Object[len];\r
- Enumeration keys = ht.keys();\r
- for (int i=0; i < len; i++)\r
- result[i] = keys.nextElement();\r
- return result;\r
- }\r
- \r
- Class getReflectedClass() {\r
- return cl;\r
- }\r
- \r
- void reflectField(Scriptable scope, Field field) {\r
- int mods = field.getModifiers();\r
- if (!Modifier.isPublic(mods))\r
- return;\r
- boolean isStatic = Modifier.isStatic(mods);\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- String name = field.getName();\r
- Object member = ht.get(name);\r
- if (member != null) {\r
- if (member instanceof NativeJavaMethod) {\r
- NativeJavaMethod method = (NativeJavaMethod) member;\r
- FieldAndMethods fam = new FieldAndMethods(method.getMethods(),\r
- field,\r
- null);\r
- fam.setPrototype(ScriptableObject.getFunctionPrototype(scope));\r
- getFieldAndMethodsTable(isStatic).put(name, fam);\r
- ht.put(name, fam);\r
- return;\r
- }\r
- if (member instanceof Field) {\r
- Field oldField = (Field) member;\r
- // If this newly reflected field shadows an inherited field, \r
- // then replace it. Otherwise, since access to the field \r
- // would be ambiguous from Java, no field should be reflected.\r
- // For now, the first field found wins, unless another field \r
- // explicitly shadows it.\r
- if (oldField.getDeclaringClass().isAssignableFrom(field.getDeclaringClass()))\r
- ht.put(name, field);\r
- return;\r
- }\r
- throw new RuntimeException("unknown member type");\r
- }\r
- ht.put(name, field);\r
- }\r
-\r
- void reflectMethod(Scriptable scope, Method method) {\r
- int mods = method.getModifiers();\r
- if (!Modifier.isPublic(mods))\r
- return;\r
- boolean isStatic = Modifier.isStatic(mods);\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- String name = method.getName();\r
- NativeJavaMethod fun = (NativeJavaMethod) ht.get(name);\r
- if (fun == null) {\r
- fun = new NativeJavaMethod();\r
- if (scope != null)\r
- fun.setPrototype(ScriptableObject.getFunctionPrototype(scope));\r
- ht.put(name, fun);\r
- fun.add(method);\r
- } else {\r
- fun.add(method);\r
- }\r
- }\r
-\r
- void reflect(Scriptable scope, Class cl) {\r
- // We reflect methods first, because we want overloaded field/method\r
- // names to be allocated to the NativeJavaMethod before the field\r
- // gets in the way.\r
- Method[] methods = cl.getMethods();\r
- for (int i = 0; i < methods.length; i++)\r
- reflectMethod(scope, methods[i]);\r
- \r
- Field[] fields = cl.getFields();\r
- for (int i = 0; i < fields.length; i++)\r
- reflectField(scope, fields[i]);\r
-\r
- makeBeanProperties(scope, false);\r
- makeBeanProperties(scope, true);\r
- \r
- ctors = cl.getConstructors();\r
- }\r
- \r
- Hashtable getFieldAndMethodsTable(boolean isStatic) {\r
- Hashtable fmht = isStatic ? staticFieldAndMethods\r
- : fieldAndMethods;\r
- if (fmht == null) {\r
- fmht = new Hashtable(11);\r
- if (isStatic)\r
- staticFieldAndMethods = fmht;\r
- else\r
- fieldAndMethods = fmht;\r
- }\r
- \r
- return fmht;\r
- }\r
-\r
- void makeBeanProperties(Scriptable scope, boolean isStatic) {\r
- Hashtable ht = isStatic ? staticMembers : members;\r
- Hashtable toAdd = new Hashtable();\r
- \r
- // Now, For each member, make "bean" properties.\r
- for (Enumeration e = ht.keys(); e.hasMoreElements(); ) {\r
- \r
- // Is this a getter?\r
- String name = (String) e.nextElement();\r
- boolean memberIsGetMethod = name.startsWith("get");\r
- boolean memberIsIsMethod = name.startsWith("is");\r
- if (memberIsGetMethod || memberIsIsMethod) {\r
- // Double check name component.\r
- String nameComponent = name.substring(memberIsGetMethod ? 3 : 2);\r
- if (nameComponent.length() == 0) \r
- continue;\r
- \r
- // Make the bean property name.\r
- String beanPropertyName = nameComponent;\r
- if (Character.isUpperCase(nameComponent.charAt(0))) {\r
- if (nameComponent.length() == 1) {\r
- beanPropertyName = nameComponent.substring(0, 1).toLowerCase();\r
- } else if (!Character.isUpperCase(nameComponent.charAt(1))) {\r
- beanPropertyName = Character.toLowerCase(nameComponent.charAt(0)) + \r
- nameComponent.substring(1);\r
- }\r
- }\r
- \r
- // If we already have a member by this name, don't do this\r
- // property.\r
- if (ht.containsKey(beanPropertyName))\r
- continue;\r
- \r
- // Get the method by this name.\r
- Object method = ht.get(name);\r
- if (!(method instanceof NativeJavaMethod))\r
- continue;\r
- NativeJavaMethod getJavaMethod = (NativeJavaMethod) method;\r
- \r
- // Grab and inspect the getter method; does it have an empty parameter list,\r
- // with a return value (eg. a getSomething() or isSomething())?\r
- Class[] params;\r
- Method[] getMethods = getJavaMethod.getMethods();\r
- Class type;\r
- if (getMethods != null && \r
- getMethods.length == 1 && \r
- (type = getMethods[0].getReturnType()) != null &&\r
- (params = getMethods[0].getParameterTypes()) != null && \r
- params.length == 0) \r
- { \r
- \r
- // Make sure the method static-ness is preserved for this property.\r
- if (isStatic && !Modifier.isStatic(getMethods[0].getModifiers()))\r
- continue;\r
- \r
- // We have a getter. Now, do we have a setter?\r
- Method setMethod = null;\r
- String setter = "set" + nameComponent;\r
- if (ht.containsKey(setter)) { \r
-\r
- // Is this value a method?\r
- method = ht.get(setter);\r
- if (method instanceof NativeJavaMethod) {\r
- \r
- //\r
- // Note: it may be preferable to allow NativeJavaMethod.findFunction()\r
- // to find the appropriate setter; unfortunately, it requires an\r
- // instance of the target arg to determine that.\r
- //\r
-\r
- // Make two passes: one to find a method with direct type assignment, \r
- // and one to find a widening conversion.\r
- NativeJavaMethod setJavaMethod = (NativeJavaMethod) method;\r
- Method[] setMethods = setJavaMethod.getMethods();\r
- for (int pass = 1; pass <= 2 && setMethod == null; ++pass) {\r
- for (int i = 0; i < setMethods.length; ++i) {\r
- if (setMethods[i].getReturnType() == void.class &&\r
- (!isStatic || Modifier.isStatic(setMethods[i].getModifiers())) &&\r
- (params = setMethods[i].getParameterTypes()) != null && \r
- params.length == 1 ) { \r
- \r
- if ((pass == 1 && params[0] == type) ||\r
- (pass == 2 && params[0].isAssignableFrom(type))) { \r
- setMethod = setMethods[i];\r
- break;\r
- }\r
- }\r
- }\r
- }\r
- }\r
- }\r
- \r
- // Make the property.\r
- BeanProperty bp = new BeanProperty(getMethods[0], setMethod);\r
- toAdd.put(beanPropertyName, bp);\r
- }\r
- }\r
- } \r
- \r
- // Add the new bean properties.\r
- for (Enumeration e = toAdd.keys(); e.hasMoreElements();) {\r
- String key = (String) e.nextElement();\r
- Object value = toAdd.get(key);\r
- ht.put(key, value);\r
- }\r
- }\r
-\r
- Hashtable getFieldAndMethodsObjects(Scriptable scope, Object javaObject,\r
- boolean isStatic) \r
- {\r
- Hashtable ht = isStatic ? staticFieldAndMethods : fieldAndMethods;\r
- if (ht == null)\r
- return null;\r
- int len = ht.size();\r
- Hashtable result = new Hashtable(Math.max(len,1));\r
- Enumeration e = ht.elements();\r
- while (len-- > 0) {\r
- FieldAndMethods fam = (FieldAndMethods) e.nextElement();\r
- fam = (FieldAndMethods) fam.clone();\r
- fam.setJavaObject(javaObject);\r
- result.put(fam.getName(), fam);\r
- }\r
- return result;\r
- }\r
-\r
- Constructor[] getConstructors() {\r
- return ctors;\r
- }\r
-\r
- static JavaMembers lookupClass(Scriptable scope, Class dynamicType,\r
- Class staticType)\r
- {\r
- Class cl = dynamicType;\r
- Hashtable ct = classTable; // use local reference to avoid synchronize\r
- JavaMembers members = (JavaMembers) ct.get(cl);\r
- if (members != null)\r
- return members;\r
- if (staticType != null && staticType != dynamicType &&\r
- !Modifier.isPublic(dynamicType.getModifiers()) &&\r
- Modifier.isPublic(staticType.getModifiers()))\r
- {\r
- cl = staticType;\r
- \r
- // We can use the static type, and that is OK, but we'll trace\r
- // back the java class chain here to look for something more suitable.\r
- for (Class parentType = dynamicType; \r
- parentType != null && parentType != ScriptRuntime.ObjectClass;\r
- parentType = parentType.getSuperclass())\r
- {\r
- if (Modifier.isPublic(parentType.getModifiers())) {\r
- cl = parentType;\r
- break;\r
- }\r
- }\r
- }\r
- try {\r
- members = new JavaMembers(scope, cl);\r
- } catch (SecurityException e) {\r
- // Reflection may fail for objects that are in a restricted \r
- // access package (e.g. sun.*). If we get a security\r
- // exception, try again with the static type. Otherwise, \r
- // rethrow the exception.\r
- if (cl != staticType)\r
- members = new JavaMembers(scope, staticType);\r
- else\r
- throw e;\r
- }\r
- if (Context.isCachingEnabled) \r
- ct.put(cl, members);\r
- return members;\r
- }\r
-\r
- RuntimeException reportMemberNotFound(String memberName) {\r
- return Context.reportRuntimeError2(\r
- "msg.java.member.not.found", cl.getName(), memberName);\r
- }\r
-\r
- static Hashtable classTable = new Hashtable();\r
-\r
- private Class cl;\r
- private Hashtable members;\r
- private Hashtable fieldAndMethods;\r
- private Hashtable staticMembers;\r
- private Hashtable staticFieldAndMethods;\r
- private Constructor[] ctors;\r
-}\r
-\r
-class BeanProperty {\r
- BeanProperty(Method getter, Method setter) {\r
- this.getter = getter;\r
- this.setter = setter;\r
- }\r
- Method getter;\r
- Method setter;\r
-}\r
-\r
-class FieldAndMethods extends NativeJavaMethod {\r
-\r
- FieldAndMethods(Method[] methods, Field field, String name) {\r
- super(methods);\r
- this.field = field;\r
- this.name = name;\r
- }\r
-\r
- void setJavaObject(Object javaObject) {\r
- this.javaObject = javaObject;\r
- }\r
-\r
- String getName() {\r
- if (field == null)\r
- return name;\r
- return field.getName();\r
- }\r
-\r
- Field getField() {\r
- return field;\r
- }\r
- \r
- public Object getDefaultValue(Class hint) {\r
- if (hint == ScriptRuntime.FunctionClass)\r
- return this;\r
- Object rval;\r
- Class type;\r
- try {\r
- rval = field.get(javaObject);\r
- type = field.getType();\r
- } catch (IllegalAccessException accEx) {\r
- throw Context.reportRuntimeError1(\r
- "msg.java.internal.private", getName());\r
- }\r
- rval = NativeJavaObject.wrap(this, rval, type);\r
- if (rval instanceof Scriptable) {\r
- rval = ((Scriptable) rval).getDefaultValue(hint);\r
- }\r
- return rval;\r
- }\r
-\r
- public Object clone() {\r
- FieldAndMethods result = new FieldAndMethods(methods, field, name);\r
- result.javaObject = javaObject;\r
- return result;\r
- }\r
- \r
- private Field field;\r
- private Object javaObject;\r
- private String name;\r
-}\r