jdk/src/share/classes/com/sun/script/javascript/JSAdapter.java
changeset 17462 c1bfafc15e02
parent 17461 84860231159b
parent 17460 19eb5d62770a
child 17463 9392f1567896
equal deleted inserted replaced
17461:84860231159b 17462:c1bfafc15e02
     1 /*
       
     2  * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 
       
    26 package com.sun.script.javascript;
       
    27 
       
    28 import sun.org.mozilla.javascript.internal.*;
       
    29 import java.util.*;
       
    30 
       
    31 /**
       
    32  * JSAdapter is java.lang.reflect.Proxy equivalent for JavaScript. JSAdapter
       
    33  * calls specially named JavaScript methods on an adaptee object when property
       
    34  * access is attempted on it.
       
    35  *
       
    36  * Example:
       
    37  *
       
    38  *    var y = {
       
    39  *                __get__    : function (name) { ... }
       
    40  *                __has__    : function (name) { ... }
       
    41  *                __put__    : function (name, value) {...}
       
    42  *                __delete__ : function (name) { ... }
       
    43  *                __getIds__ : function () { ... }
       
    44  *            };
       
    45  *
       
    46  *    var x = new JSAdapter(y);
       
    47  *
       
    48  *    x.i;                        // calls y.__get__
       
    49  *    i in x;                     // calls y.__has__
       
    50  *    x.p = 10;                   // calls y.__put__
       
    51  *    delete x.p;                 // calls y.__delete__
       
    52  *    for (i in x) { print(i); }  // calls y.__getIds__
       
    53  *
       
    54  * If a special JavaScript method is not found in the adaptee, then JSAdapter
       
    55  * forwards the property access to the adaptee itself.
       
    56  *
       
    57  * JavaScript caller of adapter object is isolated from the fact that
       
    58  * the property access/mutation/deletion are really calls to
       
    59  * JavaScript methods on adaptee.  Use cases include 'smart'
       
    60  * properties, property access tracing/debugging, encaptulation with
       
    61  * easy client access - in short JavaScript becomes more "Self" like.
       
    62  *
       
    63  * Note that Rhino already supports special properties like __proto__
       
    64  * (to set, get prototype), __parent__ (to set, get parent scope). We
       
    65  * follow the same double underscore nameing convention here. Similarly
       
    66  * the name JSAdapter is derived from JavaAdapter -- which is a facility
       
    67  * to extend, implement Java classes/interfaces by JavaScript.
       
    68  *
       
    69  * @author A. Sundararajan
       
    70  * @since 1.6
       
    71  */
       
    72 public final class JSAdapter implements Scriptable, Function {
       
    73     private JSAdapter(Scriptable obj) {
       
    74         setAdaptee(obj);
       
    75     }
       
    76 
       
    77     // initializer to setup JSAdapter prototype in the given scope
       
    78     public static void init(Context cx, Scriptable scope, boolean sealed)
       
    79     throws RhinoException {
       
    80         JSAdapter obj = new JSAdapter(cx.newObject(scope));
       
    81         obj.setParentScope(scope);
       
    82         obj.setPrototype(getFunctionPrototype(scope));
       
    83         obj.isPrototype = true;
       
    84         ScriptableObject.defineProperty(scope, "JSAdapter",  obj,
       
    85                 ScriptableObject.DONTENUM);
       
    86     }
       
    87 
       
    88     public String getClassName() {
       
    89         return "JSAdapter";
       
    90     }
       
    91 
       
    92     public Object get(String name, Scriptable start) {
       
    93         Function func = getAdapteeFunction(GET_PROP);
       
    94         if (func != null) {
       
    95             return call(func, new Object[] { name });
       
    96         } else {
       
    97             start = getAdaptee();
       
    98             return start.get(name, start);
       
    99         }
       
   100     }
       
   101 
       
   102     public Object get(int index, Scriptable start) {
       
   103         Function func = getAdapteeFunction(GET_PROP);
       
   104         if (func != null) {
       
   105             return call(func, new Object[] { new Integer(index) });
       
   106         } else {
       
   107             start = getAdaptee();
       
   108             return start.get(index, start);
       
   109         }
       
   110     }
       
   111 
       
   112     public boolean has(String name, Scriptable start) {
       
   113         Function func = getAdapteeFunction(HAS_PROP);
       
   114         if (func != null) {
       
   115             Object res = call(func, new Object[] { name });
       
   116             return Context.toBoolean(res);
       
   117         } else {
       
   118             start = getAdaptee();
       
   119             return start.has(name, start);
       
   120         }
       
   121     }
       
   122 
       
   123     public boolean has(int index, Scriptable start) {
       
   124         Function func = getAdapteeFunction(HAS_PROP);
       
   125         if (func != null) {
       
   126             Object res = call(func, new Object[] { new Integer(index) });
       
   127             return Context.toBoolean(res);
       
   128         } else {
       
   129             start = getAdaptee();
       
   130             return start.has(index, start);
       
   131         }
       
   132     }
       
   133 
       
   134     public void put(String name, Scriptable start, Object value) {
       
   135         if (start == this) {
       
   136             Function func = getAdapteeFunction(PUT_PROP);
       
   137             if (func != null) {
       
   138                 call(func, new Object[] { name, value });
       
   139             } else {
       
   140                 start = getAdaptee();
       
   141                 start.put(name, start, value);
       
   142             }
       
   143         } else {
       
   144             start.put(name, start, value);
       
   145         }
       
   146     }
       
   147 
       
   148     public void put(int index, Scriptable start, Object value) {
       
   149         if (start == this) {
       
   150             Function func = getAdapteeFunction(PUT_PROP);
       
   151             if( func != null) {
       
   152                 call(func, new Object[] { new Integer(index), value });
       
   153             } else {
       
   154                 start = getAdaptee();
       
   155                 start.put(index, start, value);
       
   156             }
       
   157         } else {
       
   158             start.put(index, start, value);
       
   159         }
       
   160     }
       
   161 
       
   162     public void delete(String name) {
       
   163         Function func = getAdapteeFunction(DEL_PROP);
       
   164         if (func != null) {
       
   165             call(func, new Object[] { name });
       
   166         } else {
       
   167             getAdaptee().delete(name);
       
   168         }
       
   169     }
       
   170 
       
   171     public void delete(int index) {
       
   172         Function func = getAdapteeFunction(DEL_PROP);
       
   173         if (func != null) {
       
   174             call(func, new Object[] { new Integer(index) });
       
   175         } else {
       
   176             getAdaptee().delete(index);
       
   177         }
       
   178     }
       
   179 
       
   180     public Scriptable getPrototype() {
       
   181         return prototype;
       
   182     }
       
   183 
       
   184     public void setPrototype(Scriptable prototype) {
       
   185         this.prototype = prototype;
       
   186     }
       
   187 
       
   188     public Scriptable getParentScope() {
       
   189         return parent;
       
   190     }
       
   191 
       
   192     public void setParentScope(Scriptable parent) {
       
   193         this.parent = parent;
       
   194     }
       
   195 
       
   196     public Object[] getIds() {
       
   197         Function func = getAdapteeFunction(GET_PROPIDS);
       
   198         if (func != null) {
       
   199             Object val = call(func, new Object[0]);
       
   200             // in most cases, adaptee would return native JS array
       
   201             if (val instanceof NativeArray) {
       
   202                 NativeArray array = (NativeArray) val;
       
   203                 Object[] res = new Object[(int)array.getLength()];
       
   204                 for (int index = 0; index < res.length; index++) {
       
   205                     res[index] = mapToId(array.get(index, array));
       
   206                 }
       
   207                 return res;
       
   208             } else if (val instanceof NativeJavaArray) {
       
   209                 // may be attempt wrapped Java array
       
   210                 Object tmp = ((NativeJavaArray)val).unwrap();
       
   211                 Object[] res;
       
   212                 if (tmp.getClass() == Object[].class) {
       
   213                     Object[]  array = (Object[]) tmp;
       
   214                     res = new Object[array.length];
       
   215                     for (int index = 0; index < array.length; index++) {
       
   216                         res[index] = mapToId(array[index]);
       
   217                     }
       
   218                 } else {
       
   219                     // just return an empty array
       
   220                     res = Context.emptyArgs;
       
   221                 }
       
   222                 return res;
       
   223             } else {
       
   224                 // some other return type, just return empty array
       
   225                 return Context.emptyArgs;
       
   226             }
       
   227         } else {
       
   228             return getAdaptee().getIds();
       
   229         }
       
   230     }
       
   231 
       
   232     public boolean hasInstance(Scriptable scriptable) {
       
   233         if (scriptable instanceof JSAdapter) {
       
   234             return true;
       
   235         } else {
       
   236             Scriptable proto = scriptable.getPrototype();
       
   237             while (proto != null) {
       
   238                 if (proto.equals(this)) return true;
       
   239                 proto = proto.getPrototype();
       
   240             }
       
   241             return false;
       
   242         }
       
   243     }
       
   244 
       
   245     public Object getDefaultValue(Class hint) {
       
   246         return getAdaptee().getDefaultValue(hint);
       
   247     }
       
   248 
       
   249     public Object call(Context cx, Scriptable scope, Scriptable thisObj,
       
   250             Object[] args)
       
   251             throws RhinoException {
       
   252         if (isPrototype) {
       
   253             return construct(cx, scope, args);
       
   254         } else {
       
   255             Scriptable tmp = getAdaptee();
       
   256             if (tmp instanceof Function) {
       
   257                 return ((Function)tmp).call(cx, scope, tmp, args);
       
   258             } else {
       
   259                 throw Context.reportRuntimeError("TypeError: not a function");
       
   260             }
       
   261         }
       
   262     }
       
   263 
       
   264     public Scriptable construct(Context cx, Scriptable scope, Object[] args)
       
   265     throws RhinoException {
       
   266         if (isPrototype) {
       
   267             Scriptable topLevel = ScriptableObject.getTopLevelScope(scope);
       
   268             JSAdapter newObj;
       
   269             if (args.length > 0) {
       
   270                 newObj = new JSAdapter(Context.toObject(args[0], topLevel));
       
   271             } else {
       
   272                 throw Context.reportRuntimeError("JSAdapter requires adaptee");
       
   273             }
       
   274             return newObj;
       
   275         } else {
       
   276             Scriptable tmp = getAdaptee();
       
   277             if (tmp instanceof Function) {
       
   278                 return ((Function)tmp).construct(cx, scope, args);
       
   279             } else {
       
   280                 throw Context.reportRuntimeError("TypeError: not a constructor");
       
   281             }
       
   282         }
       
   283     }
       
   284 
       
   285     public Scriptable getAdaptee() {
       
   286         return adaptee;
       
   287     }
       
   288 
       
   289     public void setAdaptee(Scriptable adaptee) {
       
   290         if (adaptee == null) {
       
   291             throw new NullPointerException("adaptee can not be null");
       
   292         }
       
   293         this.adaptee = adaptee;
       
   294     }
       
   295 
       
   296     //-- internals only below this point
       
   297 
       
   298     // map a property id. Property id can only be an Integer or String
       
   299     private Object mapToId(Object tmp) {
       
   300         if (tmp instanceof Double) {
       
   301             return new Integer(((Double)tmp).intValue());
       
   302         } else {
       
   303             return Context.toString(tmp);
       
   304         }
       
   305     }
       
   306 
       
   307     private static Scriptable getFunctionPrototype(Scriptable scope) {
       
   308         return ScriptableObject.getFunctionPrototype(scope);
       
   309     }
       
   310 
       
   311     private Function getAdapteeFunction(String name) {
       
   312         Object o = ScriptableObject.getProperty(getAdaptee(), name);
       
   313         return (o instanceof Function)? (Function)o : null;
       
   314     }
       
   315 
       
   316     private Object call(Function func, Object[] args) {
       
   317         Context cx = Context.getCurrentContext();
       
   318         Scriptable thisObj = getAdaptee();
       
   319         Scriptable scope = func.getParentScope();
       
   320         try {
       
   321             return func.call(cx, scope, thisObj, args);
       
   322         } catch (RhinoException re) {
       
   323             throw Context.reportRuntimeError(re.getMessage());
       
   324         }
       
   325     }
       
   326 
       
   327     private Scriptable prototype;
       
   328     private Scriptable parent;
       
   329     private Scriptable adaptee;
       
   330     private boolean isPrototype;
       
   331 
       
   332     // names of adaptee JavaScript functions
       
   333     private static final String GET_PROP = "__get__";
       
   334     private static final String HAS_PROP = "__has__";
       
   335     private static final String PUT_PROP = "__put__";
       
   336     private static final String DEL_PROP = "__delete__";
       
   337     private static final String GET_PROPIDS = "__getIds__";
       
   338 }