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-1999
022: * the Initial Developer. All Rights Reserved.
023: *
024: * Contributor(s):
025: * Igor Bukanov
026: *
027: * Alternatively, the contents of this file may be used under the terms of
028: * the GNU General Public License Version 2 or later (the "GPL"), in which
029: * case the provisions of the GPL are applicable instead of those above. If
030: * you wish to allow use of your version of this file only under the terms of
031: * the GPL and not to allow others to use your version of this file under the
032: * MPL, indicate your decision by deleting the provisions above and replacing
033: * them with the notice and other provisions required by the GPL. If you do
034: * not delete the provisions above, a recipient may use your version of this
035: * file under either the MPL or the GPL.
036: *
037: * ***** END LICENSE BLOCK ***** */
038:
039: package org.mozilla.javascript;
040:
041: import java.lang.reflect.Method;
042:
043: /**
044: * Adapter to use JS function as implementation of Java interfaces with
045: * single method or multiple methods with the same signature.
046: */
047: public class InterfaceAdapter {
048: private final Object proxyHelper;
049:
050: /**
051: * Make glue object implementing interface cl that will
052: * call the supplied JS function when called.
053: * Only interfaces were all methods have the same signature is supported.
054: *
055: * @return The glue object or null if <tt>cl</tt> is not interface or
056: * has methods with different signatures.
057: */
058: static Object create(Context cx, Class cl, Callable function) {
059: if (!cl.isInterface())
060: throw new IllegalArgumentException();
061:
062: Scriptable topScope = ScriptRuntime.getTopCallScope(cx);
063: ClassCache cache = ClassCache.get(topScope);
064: InterfaceAdapter adapter;
065: adapter = (InterfaceAdapter) cache.getInterfaceAdapter(cl);
066: ContextFactory cf = cx.getFactory();
067: if (adapter == null) {
068: Method[] methods = cl.getMethods();
069: if (methods.length == 0) {
070: throw Context.reportRuntimeError2(
071: "msg.no.empty.interface.conversion", String
072: .valueOf(function), cl.getClass()
073: .getName());
074: }
075: boolean canCallFunction = false;
076: canCallFunctionChecks: {
077: Class[] argTypes = methods[0].getParameterTypes();
078: // check that the rest of methods has the same signature
079: for (int i = 1; i != methods.length; ++i) {
080: Class[] types2 = methods[i].getParameterTypes();
081: if (types2.length != argTypes.length) {
082: break canCallFunctionChecks;
083: }
084: for (int j = 0; j != argTypes.length; ++j) {
085: if (types2[j] != argTypes[j]) {
086: break canCallFunctionChecks;
087: }
088: }
089: }
090: canCallFunction = true;
091: }
092: if (!canCallFunction) {
093: throw Context.reportRuntimeError2(
094: "msg.no.function.interface.conversion", String
095: .valueOf(function), cl.getClass()
096: .getName());
097: }
098: adapter = new InterfaceAdapter(cf, cl);
099: cache.cacheInterfaceAdapter(cl, adapter);
100: }
101: return VMBridge.instance.newInterfaceProxy(adapter.proxyHelper,
102: cf, adapter, function, topScope);
103: }
104:
105: private InterfaceAdapter(ContextFactory cf, Class cl) {
106: this .proxyHelper = VMBridge.instance.getInterfaceProxyHelper(
107: cf, new Class[] { cl });
108: }
109:
110: public Object invoke(ContextFactory cf, final Object target,
111: final Scriptable topScope, final Method method,
112: final Object[] args) {
113: ContextAction action = new ContextAction() {
114: public Object run(Context cx) {
115: return invokeImpl(cx, target, topScope, method, args);
116: }
117: };
118: return cf.call(action);
119: }
120:
121: Object invokeImpl(Context cx, Object target, Scriptable topScope,
122: Method method, Object[] args) {
123: int N = (args == null) ? 0 : args.length;
124:
125: Callable function = (Callable) target;
126: Scriptable this Obj = topScope;
127: Object[] jsargs = new Object[N + 1];
128: jsargs[N] = method.getName();
129: if (N != 0) {
130: WrapFactory wf = cx.getWrapFactory();
131: for (int i = 0; i != N; ++i) {
132: jsargs[i] = wf.wrap(cx, topScope, args[i], null);
133: }
134: }
135:
136: Object result = function.call(cx, topScope, this Obj, jsargs);
137: Class javaResultType = method.getReturnType();
138: if (javaResultType == Void.TYPE) {
139: result = null;
140: } else {
141: result = Context.jsToJava(result, javaResultType);
142: }
143: return result;
144: }
145: }
|