forked from kohsuke/com4j
-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathEventProxy.java
More file actions
executable file
·196 lines (168 loc) · 5.8 KB
/
EventProxy.java
File metadata and controls
executable file
·196 lines (168 loc) · 5.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
package com4j;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.util.Map;
import java.util.HashMap;
import java.util.WeakHashMap;
import java.util.Collections;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/**
* For receiving events from COM, we use one native object (CEventReceiver)
* to handle things in the native side, and this to handle things in the Java side.
*
*
* <p>
* {@code T} is the event interface.
*
* @author Kohsuke Kawaguchi
*/
final class EventProxy<T> implements EventCookie {
private final EventInterfaceDescriptor<T> descriptor;
private final T javaObject;
private final ComThread thread;
/**
* Pointer to the native proxy.
*/
long nativeProxy;
/**
* Creates a new event proxy that implements the event interface {@code intf}
* and delivers events to {@code javaObject}.
*/
EventProxy(Class<T> intf, T javaObject, ComThread thread) {
this.descriptor = getDescriptor(intf);
this.javaObject = javaObject;
this.thread = thread;
}
/**
* Terminates the event subscription.
*/
public void close() {
if(nativeProxy!=0) {
new Task<Void>() {
public Void call() {
Native.unadvise(nativeProxy);
return null;
}
}.execute(thread);
nativeProxy = 0;
}
}
int[] getDISPIDs(String[] names) {
int[] r = new int[names.length];
for( int i=0; i<names.length; i++ ) {
r[i] = descriptor.getDISPID(names[i]);
}
return r;
}
Object invoke(int dispId, int flag, Variant[] args) throws Throwable {
EventMethod m = descriptor.get(dispId);
if(m==null)
throw new ComException("Undefined DISPID="+dispId,DISP_E_MEMBERNOTFOUND);
return m.invoke(javaObject,flag,args);
}
//
// Used by the native code to assist exception handling
//
static String getErrorSource(Throwable t) {
return t.toString();
}
static String getErrorDetail(Throwable t) {
StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
/**
* Descriptor cache.
*/
private static final Map<Class,EventInterfaceDescriptor> descriptors =
Collections.synchronizedMap(new WeakHashMap<Class,EventInterfaceDescriptor>());
/**
* Gets the descriptor for the given type.
*/
private static <T> EventInterfaceDescriptor<T> getDescriptor(Class<T> t) {
EventInterfaceDescriptor<T> r = descriptors.get(t);
if(r==null) {
r = new EventInterfaceDescriptor<T>(t);
descriptors.put(t,r);
}
return r;
}
/**
* Describes the event interface.
*/
private static class EventInterfaceDescriptor<T> {
private final Class<T> eventInterface;
/**
* Methods by their names.
*/
private final Map<String,EventMethod> methodsByName = new HashMap<String,EventMethod>();
private final Map<Integer,EventMethod> methodsByID = new HashMap<Integer, EventMethod>();
EventInterfaceDescriptor(Class<T> eventInterface) {
this.eventInterface = eventInterface;
for (Method m : eventInterface.getDeclaredMethods()) {
EventMethod em = new EventMethod(m);
methodsByName.put(m.getName(),em);
methodsByID.put(em.dispid,em);
}
}
public int getDISPID(String name) {
EventMethod r = methodsByName.get(name);
if(r==null)
return DISP_E_UNKNOWNNAME;
else
return r.dispid;
}
public EventMethod get(int id) {
return methodsByID.get(id);
}
}
private static class EventMethod {
private final int dispid;
private final Method method;
private final Class<?>[] params;
public EventMethod(Method m) {
DISPID a = m.getAnnotation(DISPID.class);
if(a==null)
throw new IllegalAnnotationException(m+" needs to have @DISPID");
dispid = a.value();
method = m;
params = m.getParameterTypes();
}
/**
* Invokes a method.
*/
public Object invoke(Object o, int flag, Variant[] args) throws Throwable {
try {
if(args.length!=params.length)
throw new ComException("Argument length mismatch. Expected "+params.length+" but found "+args.length,DISP_E_BADPARAMCOUNT);
Object[] oargs = new Object[args.length];
for( int i=0; i<args.length; i++ )
oargs[i] = args[i].convertTo(params[i]);
return method.invoke(o,oargs);
} catch (InvocationTargetException e) {
logger.log(Level.WARNING, method+" on "+o+" reported an exception",e.getTargetException());
throw e.getTargetException();
} catch (RuntimeException e) {
logger.log(Level.WARNING, "Unable to invoke "+method+" on "+o,e);
throw e;
}
}
}
private static final int DISP_E_UNKNOWNNAME = 0x80020006;
private static final int DISP_E_MEMBERNOTFOUND = 0x80020003;
private static final int DISP_E_BADPARAMCOUNT = 0x8002000E;
private static final Logger logger = Logger.getLogger(EventProxy.class.getName());
static {
boolean com4jDebug = false;
try {
com4jDebug = System.getProperty("com4j.debug")!=null;
} catch (Throwable e) {
;
}
if(!com4jDebug)
logger.setLevel(Level.OFF);
}
}