001: /*
002: * Copyright 2006 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.JavaDispatch;
020: import com.google.gwt.dev.shell.JavaDispatchImpl;
021: import com.google.gwt.dev.shell.JsValueGlue;
022: import com.google.gwt.dev.shell.LowLevel;
023:
024: import org.eclipse.swt.internal.ole.win32.COM;
025: import org.eclipse.swt.internal.ole.win32.IDispatch;
026: import org.eclipse.swt.ole.win32.Variant;
027:
028: import java.lang.reflect.Field;
029: import java.lang.reflect.InvocationTargetException;
030: import java.lang.reflect.Method;
031:
032: /**
033: * Wraps an arbitrary Java Object as an Automation-compatible server. The class
034: * was motivated by the need to expose Java objects into JavaScript.
035: *
036: * <p>
037: * <b>Features</b>
038: * </p>
039: * <ul>
040: * <li>Implements the <code>IDispatch</code> interface for you</li>
041: * <li>If the COM client keeps a reference to this object, this object is
042: * prevented from being garbage collected</li>
043: * <li>Manages a JNI global ref on the target object so that it can be
044: * reliabily passed back and forth between Java and external code </li>
045: * <li>An instance of this class with no target is used to globally access all
046: * static methods or fields.</li>
047: * </ul>
048: *
049: * <p>
050: * <b>Limitations</b>
051: * </p>
052: * <ul>
053: * <li>Only late-bound dispatch is supported</li>
054: * <li>Named arguments are not supported (see {@link #GetIDsOfNames})).</li>
055: * </ul>
056: */
057: class IDispatchProxy extends IDispatchImpl {
058:
059: // A magic dispid for getting a global ref to this object.
060: public static final int DISPID_MAGIC_GETGLOBALREF = 0xC131FB56;
061:
062: private final CompilingClassLoader classLoader;
063:
064: private boolean isDisposed = false;
065:
066: private final JavaDispatch javaDispatch;
067:
068: private final int myGlobalRef;
069:
070: /**
071: * This constructor initializes as the static dispatcher, which handles only
072: * static method calls and field references.
073: *
074: * @param cl this class's classLoader
075: */
076: IDispatchProxy(CompilingClassLoader cl) {
077: javaDispatch = new JavaDispatchImpl(cl);
078: classLoader = cl;
079: myGlobalRef = 0;
080: }
081:
082: /**
083: * This constructor initializes a dispatcher, around a particular instance.
084: *
085: * @param cl this class's classLoader
086: * @param target the object being wrapped as an IDispatch
087: */
088: IDispatchProxy(CompilingClassLoader cl, Object target) {
089: javaDispatch = new JavaDispatchImpl(cl, target);
090: classLoader = cl;
091: myGlobalRef = LowLevel.newGlobalRefInt(this );
092: }
093:
094: /**
095: * Must be called when the object is no longer needed (to release the global
096: * reference on the target object).
097: */
098: @Override
099: public void dispose() {
100: // Release the global ref on myself.
101: if (myGlobalRef != 0) {
102: LowLevel.deleteGlobalRefInt(myGlobalRef);
103: }
104: super .dispose();
105: isDisposed = true;
106: }
107:
108: public Object getTarget() {
109: return javaDispatch.getTarget();
110: }
111:
112: /**
113: * Determine whether the proxy has already been disposed (this shouldn't be
114: * necessary, but is needed by ModuleSpaceIE6 to workaround a bug in IE).
115: */
116: public boolean isDisposed() {
117: return isDisposed;
118: }
119:
120: @Override
121: protected void getIDsOfNames(String[] names, int[] ids)
122: throws HResultException {
123: ids[0] = classLoader.getDispId(names[0]);
124: if (ids[0] == -1 || names.length >= 2) {
125: throw new HResultException(DISP_E_UNKNOWNNAME);
126: }
127: }
128:
129: @Override
130: protected Variant invoke(int dispId, int flags, Variant[] params)
131: throws HResultException, InvocationTargetException {
132: try {
133: // Whatever the caller asks for, try to find it via reflection.
134: //
135: if (dispId == DISPID_MAGIC_GETGLOBALREF && myGlobalRef != 0) {
136: // Handle specially.
137: //
138: return new Variant(myGlobalRef);
139: } else if (dispId == 0) {
140: if ((flags & COM.DISPATCH_METHOD) != 0) {
141: // implicit call -- "m()"
142: // not supported -- fall through to unsupported failure
143: } else if ((flags & COM.DISPATCH_PROPERTYGET) != 0) {
144: // implicit toString -- "'foo' + m"
145: return new Variant(getTarget().toString());
146: }
147:
148: } else if (dispId > 0) {
149: if (javaDispatch.isMethod(dispId)) {
150: Method method = javaDispatch.getMethod(dispId);
151: if ((flags & COM.DISPATCH_METHOD) != 0) {
152: // This is a method call.
153: return callMethod(classLoader, getTarget(),
154: params, method);
155: } else if (flags == COM.DISPATCH_PROPERTYGET) {
156: // The function is being accessed as a property.
157: IDispatchImpl dispMethod = (IDispatchImpl) classLoader
158: .getMethodDispatch(method);
159: if (dispMethod == null) {
160: dispMethod = new MethodDispatch(
161: classLoader, method);
162: classLoader.putMethodDispatch(method,
163: dispMethod);
164: }
165: IDispatch disp = new IDispatch(dispMethod
166: .getAddress());
167: disp.AddRef();
168: return new Variant(disp);
169: }
170: } else if (javaDispatch.isField(dispId)) {
171: Field field = javaDispatch.getField(dispId);
172: if (flags == COM.DISPATCH_PROPERTYGET) {
173: return SwtOleGlue.convertObjectToVariant(
174: classLoader, field.getType(),
175: javaDispatch.getFieldValue(dispId));
176: } else if ((flags & (COM.DISPATCH_PROPERTYPUT | COM.DISPATCH_PROPERTYPUTREF)) != 0) {
177: javaDispatch.setFieldValue(dispId, JsValueGlue
178: .get(new JsValueIE6(params[0]), field
179: .getType(), "Setting field '"
180: + field.getName() + "'"));
181: return new Variant();
182: }
183: }
184: } else {
185: // The specified member id is out of range.
186: throw new HResultException(COM.DISP_E_MEMBERNOTFOUND);
187: }
188: } catch (IllegalArgumentException e) {
189: // should never, ever happen
190: e.printStackTrace();
191: throw new HResultException(e);
192: }
193:
194: System.err
195: .println("IDispatchProxy cannot be invoked with flags: "
196: + Integer.toHexString(flags));
197: throw new HResultException(COM.E_NOTSUPPORTED);
198: }
199: }
|