001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.shell.ie;
017:
018: import com.google.gwt.dev.shell.CompilingClassLoader;
019: import com.google.gwt.dev.shell.JsValueGlue;
020:
021: import org.eclipse.swt.internal.ole.win32.COM;
022: import org.eclipse.swt.internal.ole.win32.IDispatch;
023: import org.eclipse.swt.ole.win32.Variant;
024:
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027:
028: /**
029: * Wraps an arbitrary Java Method as an Automation-compatible server. The class
030: * was motivated by the need to expose Java objects into JavaScript.
031: *
032: * <p>
033: * <b>Features</b>
034: * </p>
035: * <ul>
036: * <li>Implements the <code>IDispatch</code> interface for you</li>
037: * <li>If the COM client keeps a reference to this object, this object is
038: * prevented from being garbage collected</li>
039: * </ul>
040: *
041: * <p>
042: * <b>Limitations</b>
043: * </p>
044: * <ul>
045: * <li>Only late-bound dispatch is supported</li>
046: * <li>Named arguments are not supported (see {@link #GetIDsOfNames})).</li>
047: * </ul>
048: */
049: class MethodDispatch extends IDispatchImpl {
050:
051: private final CompilingClassLoader classLoader;
052:
053: private final Method method;
054:
055: public MethodDispatch(CompilingClassLoader classLoader,
056: Method method) {
057: this .classLoader = classLoader;
058: this .method = method;
059: }
060:
061: @Override
062: public String toString() {
063: return "\nfunction " + method.toString()
064: + "(){\n [native code]\n}\n";
065: }
066:
067: /**
068: * ID 0 is magic. It can either mean toString or invoke, depending on the
069: * flags. So we start with ID 1 for toString. {@link IDispatchProxy} and
070: * {@link BrowserWidgetIE6.External} should be fixed to do the same.
071: */
072: @Override
073: protected void getIDsOfNames(String[] names, int[] ids)
074: throws HResultException {
075: if (names[0].equalsIgnoreCase("toString")) {
076: ids[0] = 1;
077: } else if (names[0].equalsIgnoreCase("call")) {
078: ids[0] = 2;
079: } else if (names[0].equalsIgnoreCase("apply")) {
080: ids[0] = 3;
081: } else {
082: throw new HResultException(IDispatchImpl.DISP_E_UNKNOWNNAME);
083: }
084: }
085:
086: /*
087: * Handles all the things the browser can do to a function object.
088: */
089: @Override
090: protected Variant invoke(int id, int flags, Variant[] params)
091: throws HResultException, InvocationTargetException {
092: switch (id) {
093: case 0:
094: // An implicit access.
095: if ((flags & COM.DISPATCH_METHOD) != 0) {
096: // implicit call -- "m()"
097: return callMethod(classLoader, null, params, method);
098: } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
099: // implicit toString -- "'foo' + m"
100: return new Variant(toString());
101: }
102: break;
103: case 1:
104: // toString
105: if ((flags & COM.DISPATCH_METHOD) != 0) {
106: // "m.toString()"
107: return new Variant(toString());
108: } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
109: // "m.toString"
110: Method toStringMethod;
111: try {
112: toStringMethod = Object.class
113: .getDeclaredMethod("toString");
114: } catch (Throwable e) {
115: throw new RuntimeException(
116: "Failed to get Object.toString() method", e);
117: }
118: IDispatchImpl dispMethod = (IDispatchImpl) classLoader
119: .getMethodDispatch(toStringMethod);
120: if (dispMethod == null) {
121: dispMethod = new MethodDispatch(classLoader,
122: toStringMethod);
123: classLoader.putMethodDispatch(toStringMethod,
124: dispMethod);
125: }
126: IDispatch disp = new IDispatch(dispMethod.getAddress());
127: disp.AddRef();
128: return new Variant(disp);
129: }
130: break;
131: case 2:
132: // call
133: if ((flags & COM.DISPATCH_METHOD) != 0) {
134: // "m.call(thisObj, arg)"
135:
136: /*
137: * First param must be a this object of the correct type (for instance
138: * methods). If method is static, it can be null.
139: */
140: Object jthis = JsValueGlue.get(
141: new JsValueIE6(params[0]), method
142: .getDeclaringClass(), "this");
143: Variant[] otherParams = new Variant[params.length - 1];
144: System.arraycopy(params, 1, otherParams, 0,
145: otherParams.length);
146: return callMethod(classLoader, jthis , otherParams,
147: method);
148: } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
149: // "m.call"
150: // TODO: not supported
151: }
152: break;
153: case 3:
154: // apply
155: // TODO: not supported
156: break;
157: case IDispatchProxy.DISPID_MAGIC_GETGLOBALREF:
158: // We are NOT in fact a "wrapped Java Object", but we don't want to
159: // throw an exception for being asked.
160: return new Variant(0);
161: default:
162: // The specified member id is out of range.
163: throw new HResultException(COM.DISP_E_MEMBERNOTFOUND);
164: }
165: throw new HResultException(COM.E_NOTSUPPORTED);
166: }
167: }
|