1 | /* |
2 | * $Id: DispatchJavaMethod.java,v 1.2 2005/12/01 06:10:00 hastings Exp $ |
3 | * |
4 | * Copyright (c) 2004, Moebius Solutions, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions |
9 | * are met: |
10 | * |
11 | * Redistributions of source code must retain the above copyright |
12 | * notice, this list of conditions and the following disclaimer. |
13 | * |
14 | * Redistributions in binary form must reproduce the above |
15 | * copyright notice, this list of conditions and the following |
16 | * disclaimer in the documentation and/or other materials provided |
17 | * with the distribution. |
18 | * |
19 | * Neither the name of Moebius Solutions, Inc. nor the names of |
20 | * its contributors may be used to endorse or promote products |
21 | * derived from this software without specific prior written |
22 | * permission. |
23 | * |
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
25 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
26 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
27 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
28 | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
29 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
30 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
31 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
32 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
33 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
34 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
35 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
36 | */ |
37 | package com.moesol.bindings; |
38 | |
39 | import java.lang.reflect.Field; |
40 | import java.lang.reflect.InvocationTargetException; |
41 | import java.lang.reflect.Method; |
42 | |
43 | import com.moesol.bindings.platform_sdk.component_services.COMException; |
44 | import com.moesol.bindings.platform_sdk.component_services.GUID; |
45 | import com.moesol.bindings.platform_sdk.component_services.HRESULT; |
46 | |
47 | /** |
48 | * Internal helper. Record the dispatch id, invocation kind, reflection method, |
49 | * method name, and method signature. This class is used by the native |
50 | * GenericIDispatch class to call the correct Java method when IDispatch::Invoke |
51 | * is called. It is also used by the ComInterfaceThunk to call the correct Java |
52 | * method when the vtable entry is thunked. |
53 | */ |
54 | public class DispatchJavaMethod { |
55 | /** |
56 | * Construct a Dispatch ID to Java Method mapping. |
57 | * |
58 | * @param disp_id |
59 | * dispatch id |
60 | * @param invkind |
61 | * invocation kind (DISPATCH_METHOD, DISPATCH_PROPERTYGET, |
62 | * DISPATCH_PROPERTYPUT, DISPATCH_PROPERTYPUTREF). |
63 | * @param name |
64 | * Java method name |
65 | * @param sig |
66 | * JNI method signature |
67 | */ |
68 | public DispatchJavaMethod(int disp_id, short invkind, String name, String sig) { |
69 | m_disp_id = disp_id; |
70 | m_invkind = invkind; |
71 | m_java_method = null; |
72 | m_method_name = name; |
73 | m_method_sig = sig; |
74 | } |
75 | |
76 | /** |
77 | * Find the Disp implementation class for an IID. This is a relatively slow |
78 | * search, so users of this method will probably want to cache the results. |
79 | * |
80 | * @param object |
81 | * A Java object we think implements an interface with an IID of |
82 | * <code>iid</code>. |
83 | * @param iid |
84 | * The GUID we are trying to find. |
85 | * @return The Class found. If we are looking for IFoo.IID then we will |
86 | * return IFoo.Disp |
87 | * @throws COMException |
88 | * if we cannot find the class. |
89 | */ |
90 | public static Class findDispClassForIID(Object object, GUID iid) { |
91 | Class[] out_class = { null }; |
92 | for (Class c = object.getClass(); c != null; c = c.getSuperclass()) { |
93 | if (findDispClassForIID(c, iid, out_class)) { |
94 | return out_class[0]; |
95 | } |
96 | } |
97 | |
98 | throw new COMException("Failed to find interface for IID, object=" |
99 | + object + " iid=" + iid, HRESULT.E_NOINTERFACE); |
100 | } |
101 | |
102 | /** |
103 | * @param object_class |
104 | * @param iid |
105 | * @return null if not found |
106 | */ |
107 | private static boolean findDispClassForIID(Class object_class, GUID iid, Class[] out_class) { |
108 | if (object_class == null) { |
109 | return false; |
110 | } |
111 | |
112 | // Gets a class's ifaces or an interface's super ifaces |
113 | Class[] ifaces = object_class.getInterfaces(); |
114 | for (int i = 0; i < ifaces.length; i++) { |
115 | if (isSameInterface(ifaces[i], iid)) { |
116 | return findDispClassFromInterface(ifaces[i], out_class); |
117 | } |
118 | |
119 | // recurse for super interfaces |
120 | if (findDispClassForIID(ifaces[i], iid, out_class)) { |
121 | return true; |
122 | } |
123 | } |
124 | return false; |
125 | } |
126 | private static boolean isSameInterface(Class iface_class, GUID iid) { |
127 | Field[] fields = iface_class.getDeclaredFields(); |
128 | for (int i = 0; i < fields.length; i++) { |
129 | if (isSameInterface(iface_class, fields[i], iid)) { |
130 | return true; |
131 | } |
132 | } |
133 | return false; |
134 | } |
135 | private static boolean isSameInterface(Class iface_class, Field field, GUID iid) { |
136 | if (!field.getName().equals("IID")) { |
137 | return false; |
138 | } |
139 | try { |
140 | GUID iid_check = (GUID)iface_class.getField("IID").get(null); |
141 | return iid_check.equals(iid); |
142 | } catch (Exception e) { |
143 | throw new RuntimeException(e); |
144 | } |
145 | } |
146 | private static boolean findDispClassFromInterface(Class iface_class, Class[] out_class) { |
147 | Class[] classes = iface_class.getDeclaredClasses(); |
148 | for (int i = 0; i < classes.length; i++) { |
149 | if (classes[i].getName().endsWith("$Disp")) { |
150 | out_class[0] = classes[i]; |
151 | return true; |
152 | } |
153 | } |
154 | |
155 | throw new COMException( |
156 | "Found IID, but missing Disp inner class, iface_class=" |
157 | + iface_class, HRESULT.E_NOINTERFACE); |
158 | } |
159 | |
160 | public boolean isMatch(int disp_id, short invkind) { |
161 | return m_disp_id == disp_id && m_invkind == invkind; |
162 | } |
163 | |
164 | /** |
165 | * Call underlying Java method. |
166 | * |
167 | * @param target |
168 | * @param args |
169 | * @return result of invoking the java method. |
170 | * @throws IllegalArgumentException |
171 | * @throws IllegalAccessException |
172 | * @throws InvocationTargetException |
173 | */ |
174 | public Object invoke(Object target, Object[] args) |
175 | throws IllegalArgumentException, IllegalAccessException, |
176 | InvocationTargetException { |
177 | return m_java_method.invoke(target, args); |
178 | } |
179 | |
180 | /** |
181 | * @return dispatch id |
182 | */ |
183 | public int getDispId() { |
184 | return m_disp_id; |
185 | } |
186 | |
187 | /** |
188 | * @return invocation kind |
189 | */ |
190 | public short getInvKind() { |
191 | return m_invkind; |
192 | } |
193 | |
194 | /** |
195 | * @return Java method's reflected Method |
196 | */ |
197 | public Method getJavaMethod() { |
198 | return m_java_method; |
199 | } |
200 | |
201 | /** |
202 | * @return Java method name. |
203 | */ |
204 | public String getMethodName() { |
205 | return m_method_name; |
206 | } |
207 | |
208 | /** |
209 | * @return Java method's JNI signature. |
210 | */ |
211 | public String getMethodSig() { |
212 | return m_method_sig; |
213 | } |
214 | |
215 | /** |
216 | * Fill in the Java Method for this mapping if not already filled in. |
217 | * |
218 | * @param iface_class |
219 | * @throws NoSuchMethodException |
220 | */ |
221 | public void fillInMethod(Class iface_class) throws NoSuchMethodException { |
222 | if (m_java_method != null) { |
223 | return; |
224 | } |
225 | m_java_method = getMethod(iface_class, m_method_name, m_method_sig); |
226 | } |
227 | |
228 | /** |
229 | * Support looking up a method by name and JNI signature from java. |
230 | * |
231 | * @param iface_class |
232 | * @param name |
233 | * @param jni_signature |
234 | * @return the found Method |
235 | * @throws NoSuchMethodException |
236 | */ |
237 | static Method getMethod(Class iface_class, String name, String jni_signature) |
238 | throws NoSuchMethodException { |
239 | return jni_getMethod(iface_class, name, jni_signature); |
240 | } |
241 | |
242 | private static native Method jni_getMethod(Class iface_class, String name, |
243 | String jni_signature) throws NoSuchMethodException; |
244 | |
245 | private int m_disp_id; |
246 | private short m_invkind; |
247 | private Method m_java_method; |
248 | private String m_method_name; |
249 | private String m_method_sig; |
250 | } |