0N/A/*
2362N/A * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage com.sun.script.javascript;
0N/A
0N/Aimport sun.org.mozilla.javascript.internal.*;
0N/Aimport java.util.*;
0N/A
0N/A/**
0N/A * JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
0N/A * calls specially named JavaScript methods on an adaptee object when property
0N/A * access is attempted on it.
0N/A *
0N/A * Example:
0N/A *
0N/A * var y = {
0N/A * __get__ : function (name) { ... }
0N/A * __has__ : function (name) { ... }
0N/A * __put__ : function (name, value) {...}
0N/A * __delete__ : function (name) { ... }
0N/A * __getIds__ : function () { ... }
0N/A * };
0N/A *
0N/A * var x = new JSAdapter(y);
0N/A *
0N/A * x.i; // calls y.__get__
0N/A * i in x; // calls y.__has__
0N/A * x.p = 10; // calls y.__put__
0N/A * delete x.p; // calls y.__delete__
0N/A * for (i in x) { print(i); } // calls y.__getIds__
0N/A *
0N/A * If a special JavaScript method is not found in the adaptee, then JSAdapter
0N/A * forwards the property access to the adaptee itself.
0N/A *
0N/A * JavaScript caller of adapter object is isolated from the fact that
0N/A * the property access/mutation/deletion are really calls to
0N/A * JavaScript methods on adaptee. Use cases include 'smart'
0N/A * properties, property access tracing/debugging, encaptulation with
0N/A * easy client access - in short JavaScript becomes more "Self" like.
0N/A *
0N/A * Note that Rhino already supports special properties like __proto__
0N/A * (to set, get prototype), __parent__ (to set, get parent scope). We
0N/A * follow the same double underscore nameing convention here. Similarly
0N/A * the name JSAdapter is derived from JavaAdapter -- which is a facility
0N/A * to extend, implement Java classes/interfaces by JavaScript.
0N/A *
0N/A * @author A. Sundararajan
0N/A * @since 1.6
0N/A */
0N/Apublic final class JSAdapter implements Scriptable, Function {
0N/A private JSAdapter(Scriptable obj) {
0N/A setAdaptee(obj);
0N/A }
0N/A
0N/A // initializer to setup JSAdapter prototype in the given scope
0N/A public static void init(Context cx, Scriptable scope, boolean sealed)
0N/A throws RhinoException {
0N/A JSAdapter obj = new JSAdapter(cx.newObject(scope));
0N/A obj.setParentScope(scope);
0N/A obj.setPrototype(getFunctionPrototype(scope));
0N/A obj.isPrototype = true;
0N/A ScriptableObject.defineProperty(scope, "JSAdapter", obj,
0N/A ScriptableObject.DONTENUM);
0N/A }
0N/A
0N/A public String getClassName() {
0N/A return "JSAdapter";
0N/A }
0N/A
0N/A public Object get(String name, Scriptable start) {
0N/A Function func = getAdapteeFunction(GET_PROP);
0N/A if (func != null) {
0N/A return call(func, new Object[] { name });
0N/A } else {
0N/A start = getAdaptee();
0N/A return start.get(name, start);
0N/A }
0N/A }
0N/A
0N/A public Object get(int index, Scriptable start) {
0N/A Function func = getAdapteeFunction(GET_PROP);
0N/A if (func != null) {
0N/A return call(func, new Object[] { new Integer(index) });
0N/A } else {
0N/A start = getAdaptee();
0N/A return start.get(index, start);
0N/A }
0N/A }
0N/A
0N/A public boolean has(String name, Scriptable start) {
0N/A Function func = getAdapteeFunction(HAS_PROP);
0N/A if (func != null) {
0N/A Object res = call(func, new Object[] { name });
0N/A return Context.toBoolean(res);
0N/A } else {
0N/A start = getAdaptee();
0N/A return start.has(name, start);
0N/A }
0N/A }
0N/A
0N/A public boolean has(int index, Scriptable start) {
0N/A Function func = getAdapteeFunction(HAS_PROP);
0N/A if (func != null) {
0N/A Object res = call(func, new Object[] { new Integer(index) });
0N/A return Context.toBoolean(res);
0N/A } else {
0N/A start = getAdaptee();
0N/A return start.has(index, start);
0N/A }
0N/A }
0N/A
0N/A public void put(String name, Scriptable start, Object value) {
0N/A if (start == this) {
0N/A Function func = getAdapteeFunction(PUT_PROP);
0N/A if (func != null) {
0N/A call(func, new Object[] { name, value });
0N/A } else {
0N/A start = getAdaptee();
0N/A start.put(name, start, value);
0N/A }
0N/A } else {
0N/A start.put(name, start, value);
0N/A }
0N/A }
0N/A
0N/A public void put(int index, Scriptable start, Object value) {
0N/A if (start == this) {
0N/A Function func = getAdapteeFunction(PUT_PROP);
0N/A if( func != null) {
0N/A call(func, new Object[] { new Integer(index), value });
0N/A } else {
0N/A start = getAdaptee();
0N/A start.put(index, start, value);
0N/A }
0N/A } else {
0N/A start.put(index, start, value);
0N/A }
0N/A }
0N/A
0N/A public void delete(String name) {
0N/A Function func = getAdapteeFunction(DEL_PROP);
0N/A if (func != null) {
0N/A call(func, new Object[] { name });
0N/A } else {
0N/A getAdaptee().delete(name);
0N/A }
0N/A }
0N/A
0N/A public void delete(int index) {
0N/A Function func = getAdapteeFunction(DEL_PROP);
0N/A if (func != null) {
0N/A call(func, new Object[] { new Integer(index) });
0N/A } else {
0N/A getAdaptee().delete(index);
0N/A }
0N/A }
0N/A
0N/A public Scriptable getPrototype() {
0N/A return prototype;
0N/A }
0N/A
0N/A public void setPrototype(Scriptable prototype) {
0N/A this.prototype = prototype;
0N/A }
0N/A
0N/A public Scriptable getParentScope() {
0N/A return parent;
0N/A }
0N/A
0N/A public void setParentScope(Scriptable parent) {
0N/A this.parent = parent;
0N/A }
0N/A
0N/A public Object[] getIds() {
0N/A Function func = getAdapteeFunction(GET_PROPIDS);
0N/A if (func != null) {
0N/A Object val = call(func, new Object[0]);
0N/A // in most cases, adaptee would return native JS array
0N/A if (val instanceof NativeArray) {
0N/A NativeArray array = (NativeArray) val;
0N/A Object[] res = new Object[(int)array.getLength()];
0N/A for (int index = 0; index < res.length; index++) {
0N/A res[index] = mapToId(array.get(index, array));
0N/A }
0N/A return res;
0N/A } else if (val instanceof NativeJavaArray) {
0N/A // may be attempt wrapped Java array
0N/A Object tmp = ((NativeJavaArray)val).unwrap();
0N/A Object[] res;
0N/A if (tmp.getClass() == Object[].class) {
0N/A Object[] array = (Object[]) tmp;
0N/A res = new Object[array.length];
0N/A for (int index = 0; index < array.length; index++) {
0N/A res[index] = mapToId(array[index]);
0N/A }
0N/A } else {
0N/A // just return an empty array
0N/A res = Context.emptyArgs;
0N/A }
0N/A return res;
0N/A } else {
0N/A // some other return type, just return empty array
0N/A return Context.emptyArgs;
0N/A }
0N/A } else {
0N/A return getAdaptee().getIds();
0N/A }
0N/A }
0N/A
0N/A public boolean hasInstance(Scriptable scriptable) {
0N/A if (scriptable instanceof JSAdapter) {
0N/A return true;
0N/A } else {
0N/A Scriptable proto = scriptable.getPrototype();
0N/A while (proto != null) {
0N/A if (proto.equals(this)) return true;
0N/A proto = proto.getPrototype();
0N/A }
0N/A return false;
0N/A }
0N/A }
0N/A
0N/A public Object getDefaultValue(Class hint) {
0N/A return getAdaptee().getDefaultValue(hint);
0N/A }
0N/A
0N/A public Object call(Context cx, Scriptable scope, Scriptable thisObj,
0N/A Object[] args)
0N/A throws RhinoException {
0N/A if (isPrototype) {
0N/A return construct(cx, scope, args);
0N/A } else {
0N/A Scriptable tmp = getAdaptee();
0N/A if (tmp instanceof Function) {
0N/A return ((Function)tmp).call(cx, scope, tmp, args);
0N/A } else {
0N/A throw Context.reportRuntimeError("TypeError: not a function");
0N/A }
0N/A }
0N/A }
0N/A
0N/A public Scriptable construct(Context cx, Scriptable scope, Object[] args)
0N/A throws RhinoException {
0N/A if (isPrototype) {
0N/A Scriptable topLevel = ScriptableObject.getTopLevelScope(scope);
0N/A JSAdapter newObj;
0N/A if (args.length > 0) {
0N/A newObj = new JSAdapter(Context.toObject(args[0], topLevel));
0N/A } else {
0N/A throw Context.reportRuntimeError("JSAdapter requires adaptee");
0N/A }
0N/A return newObj;
0N/A } else {
0N/A Scriptable tmp = getAdaptee();
0N/A if (tmp instanceof Function) {
0N/A return ((Function)tmp).construct(cx, scope, args);
0N/A } else {
0N/A throw Context.reportRuntimeError("TypeError: not a constructor");
0N/A }
0N/A }
0N/A }
0N/A
0N/A public Scriptable getAdaptee() {
0N/A return adaptee;
0N/A }
0N/A
0N/A public void setAdaptee(Scriptable adaptee) {
0N/A if (adaptee == null) {
0N/A throw new NullPointerException("adaptee can not be null");
0N/A }
0N/A this.adaptee = adaptee;
0N/A }
0N/A
0N/A //-- internals only below this point
0N/A
0N/A // map a property id. Property id can only be an Integer or String
0N/A private Object mapToId(Object tmp) {
0N/A if (tmp instanceof Double) {
0N/A return new Integer(((Double)tmp).intValue());
0N/A } else {
0N/A return Context.toString(tmp);
0N/A }
0N/A }
0N/A
0N/A private static Scriptable getFunctionPrototype(Scriptable scope) {
0N/A return ScriptableObject.getFunctionPrototype(scope);
0N/A }
0N/A
0N/A private Function getAdapteeFunction(String name) {
0N/A Object o = ScriptableObject.getProperty(getAdaptee(), name);
0N/A return (o instanceof Function)? (Function)o : null;
0N/A }
0N/A
0N/A private Object call(Function func, Object[] args) {
0N/A Context cx = Context.getCurrentContext();
0N/A Scriptable thisObj = getAdaptee();
0N/A Scriptable scope = func.getParentScope();
0N/A try {
0N/A return func.call(cx, scope, thisObj, args);
0N/A } catch (RhinoException re) {
0N/A throw Context.reportRuntimeError(re.getMessage());
0N/A }
0N/A }
0N/A
0N/A private Scriptable prototype;
0N/A private Scriptable parent;
0N/A private Scriptable adaptee;
0N/A private boolean isPrototype;
0N/A
0N/A // names of adaptee JavaScript functions
0N/A private static final String GET_PROP = "__get__";
0N/A private static final String HAS_PROP = "__has__";
0N/A private static final String PUT_PROP = "__put__";
0N/A private static final String DEL_PROP = "__delete__";
0N/A private static final String GET_PROPIDS = "__getIds__";
0N/A}