1 /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
\r
3 * The contents of this file are subject to the Netscape Public
\r
4 * License Version 1.1 (the "License"); you may not use this file
\r
5 * except in compliance with the License. You may obtain a copy of
\r
6 * the License at http://www.mozilla.org/NPL/
\r
8 * Software distributed under the License is distributed on an "AS
\r
9 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
\r
10 * implied. See the License for the specific language governing
\r
11 * rights and limitations under the License.
\r
13 * The Original Code is Rhino code, released
\r
16 * The Initial Developer of the Original Code is Netscape
\r
17 * Communications Corporation. Portions created by Netscape are
\r
18 * Copyright (C) 1997-1999 Netscape Communications Corporation. All
\r
27 * Alternatively, the contents of this file may be used under the
\r
28 * terms of the GNU Public License (the "GPL"), in which case the
\r
29 * provisions of the GPL are applicable instead of those above.
\r
30 * If you wish to allow use of your version of this file only
\r
31 * under the terms of the GPL and not to allow others to use your
\r
32 * version of this file under the NPL, indicate your decision by
\r
33 * deleting the provisions above and replace them with the notice
\r
34 * and other provisions required by the GPL. If you do not delete
\r
35 * the provisions above, a recipient may use your version of this
\r
36 * file under either the NPL or the GPL.
\r
39 package org.mozilla.javascript;
\r
41 import java.lang.reflect.*;
\r
42 import java.util.Hashtable;
\r
45 * This class reflects Java classes into the JavaScript environment, mainly
\r
46 * for constructors and static members. We lazily reflect properties,
\r
47 * and currently do not guarantee that a single j.l.Class is only
\r
48 * reflected once into the JS environment, although we should.
\r
49 * The only known case where multiple reflections
\r
50 * are possible occurs when a j.l.Class is wrapped as part of a
\r
51 * method return or property access, rather than by walking the
\r
52 * Packages/java tree.
\r
54 * @author Mike Shaver
\r
55 * @see NativeJavaArray
\r
56 * @see NativeJavaObject
\r
57 * @see NativeJavaPackage
\r
60 public class NativeJavaClass extends NativeJavaObject implements Function {
\r
62 public NativeJavaClass(Scriptable scope, Class cl) {
\r
63 super(scope, cl, JavaMembers.lookupClass(scope, cl, cl));
\r
64 fieldAndMethods = members.getFieldAndMethodsObjects(this, javaObject,
\r
68 public String getClassName() {
\r
72 public boolean has(String name, Scriptable start) {
\r
73 return members.has(name, true);
\r
76 public Object get(String name, Scriptable start) {
\r
77 // When used as a constructor, ScriptRuntime.newObject() asks
\r
78 // for our prototype to create an object of the correct type.
\r
79 // We don't really care what the object is, since we're returning
\r
80 // one constructed out of whole cloth, so we return null.
\r
82 if (name.equals("prototype"))
\r
85 Object result = Scriptable.NOT_FOUND;
\r
87 if (fieldAndMethods != null) {
\r
88 result = fieldAndMethods.get(name);
\r
93 if (members.has(name, true)) {
\r
94 result = members.get(this, name, javaObject, true);
\r
96 // experimental: look for nested classes by appending $name to current class' name.
\r
98 String nestedName = getClassObject().getName() + '$' + name;
\r
99 Class nestedClass = ScriptRuntime.loadClassName(nestedName);
\r
100 Scriptable nestedValue = wrap(ScriptableObject.getTopLevelScope(this), nestedClass);
\r
101 nestedValue.setParentScope(this);
\r
102 result = nestedValue;
\r
103 } catch (ClassNotFoundException ex) {
\r
104 throw members.reportMemberNotFound(name);
\r
105 } catch (IllegalArgumentException e) {
\r
106 throw members.reportMemberNotFound(name);
\r
113 public void put(String name, Scriptable start, Object value) {
\r
114 members.put(this, name, javaObject, value, true);
\r
117 public Object[] getIds() {
\r
118 return members.getIds(true);
\r
121 public Class getClassObject() {
\r
122 return (Class) super.unwrap();
\r
126 public static NativeJavaClass wrap(Scriptable scope, Class cls) {
\r
127 return new NativeJavaClass(scope, cls);
\r
130 public Object getDefaultValue(Class hint) {
\r
131 if (hint == null || hint == ScriptRuntime.StringClass)
\r
132 return this.toString();
\r
133 if (hint == ScriptRuntime.BooleanClass)
\r
134 return Boolean.TRUE;
\r
135 if (hint == ScriptRuntime.NumberClass)
\r
136 return ScriptRuntime.NaNobj;
\r
140 public Object call(Context cx, Scriptable scope, Scriptable thisObj,
\r
142 throws JavaScriptException
\r
144 // If it looks like a "cast" of an object to this class type,
\r
145 // walk the prototype chain to see if there's a wrapper of a
\r
146 // object that's an instanceof this class.
\r
147 if (args.length == 1 && args[0] instanceof Scriptable) {
\r
148 Class c = getClassObject();
\r
149 Scriptable p = (Scriptable) args[0];
\r
151 if (p instanceof Wrapper) {
\r
152 Object o = ((Wrapper) p).unwrap();
\r
153 if (c.isInstance(o))
\r
156 p = p.getPrototype();
\r
157 } while (p != null);
\r
159 return construct(cx, scope, args);
\r
162 public Scriptable construct(Context cx, Scriptable scope, Object[] args)
\r
163 throws JavaScriptException
\r
165 Class classObject = getClassObject();
\r
166 int modifiers = classObject.getModifiers();
\r
167 if (! (Modifier.isInterface(modifiers) ||
\r
168 Modifier.isAbstract(modifiers)))
\r
170 Constructor[] ctors = members.getConstructors();
\r
171 Member member = NativeJavaMethod.findFunction(ctors, args);
\r
172 Constructor ctor = (Constructor) member;
\r
173 if (ctor == null) {
\r
174 String sig = NativeJavaMethod.scriptSignature(args);
\r
175 throw Context.reportRuntimeError2(
\r
176 "msg.no.java.ctor", classObject.getName(), sig);
\r
179 // Found the constructor, so try invoking it.
\r
180 return NativeJavaClass.constructSpecific(cx, scope,
\r
183 Scriptable topLevel = ScriptableObject.getTopLevelScope(this);
\r
186 // trying to construct an interface; use JavaAdapter to
\r
187 // construct a new class on the fly that implements this
\r
189 Object v = topLevel.get("JavaAdapter", topLevel);
\r
190 if (v != NOT_FOUND) {
\r
191 Function f = (Function) v;
\r
192 Object[] adapterArgs = { this, args[0] };
\r
193 return (Scriptable) f.construct(cx, topLevel,
\r
196 } catch (Exception ex) {
\r
197 // fall through to error
\r
198 String m = ex.getMessage();
\r
202 throw Context.reportRuntimeError2(
\r
203 "msg.cant.instantiate", msg, classObject.getName());
\r
207 public static Scriptable constructSpecific(Context cx,
\r
209 Scriptable thisObj,
\r
212 throws JavaScriptException
\r
214 Scriptable topLevel = ScriptableObject.getTopLevelScope(thisObj);
\r
215 Class classObject = ctor.getDeclaringClass();
\r
217 Class[] paramTypes = ctor.getParameterTypes();
\r
218 for (int i = 0; i < args.length; i++) {
\r
219 args[i] = NativeJavaObject.coerceType(paramTypes[i], args[i]);
\r
222 // we need to force this to be wrapped, because construct _has_
\r
223 // to return a scriptable
\r
225 (Scriptable) NativeJavaObject.wrap(topLevel,
\r
226 ctor.newInstance(args),
\r
229 } catch (InstantiationException instEx) {
\r
230 throw Context.reportRuntimeError2(
\r
231 "msg.cant.instantiate",
\r
232 instEx.getMessage(), classObject.getName());
\r
233 } catch (IllegalArgumentException argEx) {
\r
234 String signature = NativeJavaMethod.scriptSignature(args);
\r
235 String ctorString = ctor.toString();
\r
236 throw Context.reportRuntimeError3(
\r
237 "msg.bad.ctor.sig", argEx.getMessage(), ctorString, signature);
\r
238 } catch (InvocationTargetException e) {
\r
239 throw JavaScriptException.wrapException(scope, e);
\r
240 } catch (IllegalAccessException accessEx) {
\r
241 throw Context.reportRuntimeError1(
\r
242 "msg.java.internal.private", accessEx.getMessage());
\r
246 public String toString() {
\r
247 return "[JavaClass " + getClassObject().getName() + "]";
\r
251 * Determines if prototype is a wrapped Java object and performs
\r
252 * a Java "instanceof".
\r
253 * Exception: if value is an instance of NativeJavaClass, it isn't
\r
254 * considered an instance of the Java class; this forestalls any
\r
255 * name conflicts between java.lang.Class's methods and the
\r
256 * static methods exposed by a JavaNativeClass.
\r
258 public boolean hasInstance(Scriptable value) {
\r
260 if (value instanceof Wrapper &&
\r
261 !(value instanceof NativeJavaClass)) {
\r
262 Object instance = ((Wrapper)value).unwrap();
\r
264 return getClassObject().isInstance(instance);
\r
267 // value wasn't something we understand
\r
271 private Hashtable fieldAndMethods;
\r
273 // beard: need a scope for finding top-level prototypes.
\r
274 private Scriptable parent;
\r