|
|||||||||||||||||||
Source file | Conditionals | Statements | Methods | TOTAL | |||||||||||||||
JFCEventManager.java | 89.1% | 93.5% | 90.5% | 91.9% |
|
1 |
package junit.extensions.jfcunit.eventdata;
|
|
2 |
|
|
3 |
import junit.extensions.jfcunit.JFCTestCase;
|
|
4 |
|
|
5 |
import java.awt.AWTEvent;
|
|
6 |
import java.awt.Toolkit;
|
|
7 |
import java.awt.event.AWTEventListener;
|
|
8 |
import java.awt.event.InputEvent;
|
|
9 |
import java.awt.event.MouseEvent;
|
|
10 |
|
|
11 |
import java.util.ArrayList;
|
|
12 |
import java.util.EventListener;
|
|
13 |
import java.util.Iterator;
|
|
14 |
|
|
15 |
import javax.swing.UIManager;
|
|
16 |
import javax.swing.event.EventListenerList;
|
|
17 |
|
|
18 |
|
|
19 |
/**
|
|
20 |
* This class provides a recording capabilities for AWTEvents.
|
|
21 |
* {@link java.awt.AWTEvent}s are translated into their coresponding
|
|
22 |
* Event Data types. An application may register a listener against
|
|
23 |
* this class, to receive the event data.
|
|
24 |
*
|
|
25 |
* This class temporarily holds events until either a different
|
|
26 |
* event type is received or a timer expires on the event.
|
|
27 |
*
|
|
28 |
* @author Kevin Wilson
|
|
29 |
*/
|
|
30 |
public final class JFCEventManager implements AWTEventListener, |
|
31 |
EventDataConstants { |
|
32 |
/**
|
|
33 |
* Event Mapping Property.
|
|
34 |
*/
|
|
35 |
public static final String EVENT_MAPPING_PROPERTY = "junit.extensions.jfcUnit.eventMapping"; |
|
36 |
|
|
37 |
/**
|
|
38 |
* Used to turn on debug info.
|
|
39 |
*/
|
|
40 |
public static final String EVENT_DEBUG = "JFCEventManager.debug"; |
|
41 |
|
|
42 |
static {
|
|
43 | 4 |
if (UIManager.get(EVENT_MAPPING_PROPERTY) == null) { |
44 | 4 |
ArrayList mapping = new ArrayList();
|
45 | 4 |
mapping.add( |
46 |
new String[] {
|
|
47 |
"junit.extensions.jfcunit.eventdata.JComboBoxMouseEventData",
|
|
48 |
"javax.swing.JList"
|
|
49 |
}); |
|
50 | 4 |
mapping.add( |
51 |
new String[] {
|
|
52 |
"junit.extensions.jfcunit.eventdata.JListMouseEventData",
|
|
53 |
"javax.swing.JList"
|
|
54 |
}); |
|
55 | 4 |
mapping.add( |
56 |
new String[] {
|
|
57 |
"junit.extensions.jfcunit.eventdata.JTableMouseEventData",
|
|
58 |
"javax.swing.JTable"
|
|
59 |
}); |
|
60 | 4 |
mapping.add( |
61 |
new String[] {
|
|
62 |
"junit.extensions.jfcunit.eventdata.JTableHeaderMouseEventData",
|
|
63 |
"javax.swing.table.JTableHeader"
|
|
64 |
}); |
|
65 | 4 |
mapping.add( |
66 |
new String[] {
|
|
67 |
"junit.extensions.jfcunit.eventdata.JTreeMouseEventData",
|
|
68 |
"javax.swing.JTree"
|
|
69 |
}); |
|
70 | 4 |
mapping.add( |
71 |
new String[] {
|
|
72 |
"junit.extensions.jfcunit.eventdata.JTabbedPaneMouseEventData",
|
|
73 |
"javax.swing.JTabbedPane"
|
|
74 |
}); |
|
75 | 4 |
mapping.add( |
76 |
new String[] {
|
|
77 |
"junit.extensions.jfcunit.eventdata.JTextComponentMouseEventData",
|
|
78 |
"javax.swing.text.JTextComponent"
|
|
79 |
}); |
|
80 | 4 |
mapping.add( |
81 |
new String[] {
|
|
82 |
"junit.extensions.jfcunit.eventdata.MouseWheelEventData",
|
|
83 |
"*"
|
|
84 |
}); |
|
85 | 4 |
mapping.add( |
86 |
new String[] {
|
|
87 |
"junit.extensions.jfcunit.eventdata.JMenuMouseEventData",
|
|
88 |
"*"
|
|
89 |
}); |
|
90 | 4 |
mapping.add( |
91 |
new String[] {
|
|
92 |
"junit.extensions.jfcunit.eventdata.MouseEventData", "*" |
|
93 |
}); |
|
94 | 4 |
mapping.add( |
95 |
new String[] {
|
|
96 |
"junit.extensions.jfcunit.eventdata.StringEventData", "*" |
|
97 |
}); |
|
98 | 4 |
mapping.add( |
99 |
new String[] {
|
|
100 |
"junit.extensions.jfcunit.eventdata.KeyEventData", "*" |
|
101 |
}); |
|
102 |
|
|
103 | 4 |
UIManager.put(EVENT_MAPPING_PROPERTY, mapping); |
104 |
} |
|
105 |
} |
|
106 |
|
|
107 |
/**
|
|
108 |
* This is a singleton class. There should never be more than
|
|
109 |
* one recording session.
|
|
110 |
*/
|
|
111 |
private static JFCEventManager s_singleton = null; |
|
112 |
|
|
113 |
/**
|
|
114 |
* Debug flag set by UIManager property JFCEventManager.debug="true".
|
|
115 |
*/
|
|
116 |
private static boolean s_debug = true; |
|
117 |
|
|
118 |
/**
|
|
119 |
* AWTEvent inputs.
|
|
120 |
*/
|
|
121 |
public static final int DEBUG_INPUT = 1; |
|
122 |
|
|
123 |
/**
|
|
124 |
* EventData outputs.
|
|
125 |
*/
|
|
126 |
public static final int DEBUG_OUTPUT = 2; |
|
127 |
|
|
128 |
/**
|
|
129 |
* EventData creations.
|
|
130 |
*/
|
|
131 |
public static final int DEBUG_CREATE = 4; |
|
132 |
|
|
133 |
/**
|
|
134 |
* All debugging types.
|
|
135 |
*/
|
|
136 |
public static final int DEBUG_ALL = 7; |
|
137 |
|
|
138 |
/**
|
|
139 |
* Debugging type to be used.
|
|
140 |
*/
|
|
141 |
private static int s_debugType = DEBUG_ALL; |
|
142 |
|
|
143 |
/**
|
|
144 |
* Pending event held for consolidation.
|
|
145 |
*/
|
|
146 |
private AbstractEventData m_pendingEvent = null; |
|
147 |
|
|
148 |
/**
|
|
149 |
* Listener list.
|
|
150 |
*/
|
|
151 |
private EventListenerList m_listenerList = new EventListenerList(); |
|
152 |
|
|
153 |
/**
|
|
154 |
* Timer {@link Thread} used to send the pending event.
|
|
155 |
*/
|
|
156 |
private Thread m_timerThread;
|
|
157 |
|
|
158 |
/**
|
|
159 |
* Recording state.
|
|
160 |
*/
|
|
161 |
private boolean m_recording = false; |
|
162 |
|
|
163 |
/**
|
|
164 |
* Hold time for the timer thread before
|
|
165 |
* firing the pending event.
|
|
166 |
*/
|
|
167 |
private long m_holdTime; |
|
168 |
|
|
169 |
/**
|
|
170 |
* Time when the last event was recorded.
|
|
171 |
*/
|
|
172 |
private volatile long m_lastEventTime = 0; |
|
173 |
|
|
174 |
/**
|
|
175 |
* Private constructor.
|
|
176 |
* The method <code>getEventManager()</code> should be used instead.
|
|
177 |
*
|
|
178 |
* @param holdTime duration to hold a pending event.
|
|
179 |
*/
|
|
180 | 4 |
private JFCEventManager(final long holdTime) { |
181 | 4 |
setHoldTime(holdTime); |
182 |
} |
|
183 |
|
|
184 |
/**
|
|
185 |
* Enable/Disable debug tracing of events.
|
|
186 |
*
|
|
187 |
* @param aValue true if debugging is to be
|
|
188 |
* turned on.
|
|
189 |
*/
|
|
190 | 1 |
public static void setDebug(final boolean aValue) { |
191 | 1 |
s_debug = aValue; |
192 |
|
|
193 | 1 |
if (s_debug) {
|
194 | 1 |
UIManager.put(EVENT_DEBUG, "true");
|
195 |
} else {
|
|
196 | 0 |
UIManager.put(EVENT_DEBUG, "false");
|
197 |
} |
|
198 |
} |
|
199 |
|
|
200 |
/**
|
|
201 |
* Get the state of debugging.
|
|
202 |
*
|
|
203 |
* @return true if debugging is enabled.
|
|
204 |
*/
|
|
205 | 43 |
public static boolean getDebug() { |
206 | 43 |
Boolean value = null;
|
207 | 43 |
Object prop = UIManager.get(EVENT_DEBUG); |
208 |
|
|
209 | 43 |
if (prop instanceof Boolean) { |
210 | 42 |
value = (Boolean) prop; |
211 | 1 |
} else if (prop instanceof String) { |
212 | 1 |
value = Boolean.valueOf((String) prop); |
213 |
} |
|
214 |
|
|
215 | 43 |
if (value == null) { |
216 | 0 |
s_debug = false;
|
217 |
} else {
|
|
218 | 43 |
s_debug = value.booleanValue(); |
219 |
} |
|
220 |
|
|
221 | 43 |
return s_debug;
|
222 |
} |
|
223 |
|
|
224 |
/**
|
|
225 |
* Set the debugging type.
|
|
226 |
*
|
|
227 |
* @param type DEBUG_INPUT, DEBUG_OUTPUT and/or DEBUG_CREATE
|
|
228 |
*/
|
|
229 | 120 |
public static void setDebugType(final int type) { |
230 | 120 |
s_debugType = type; |
231 |
} |
|
232 |
|
|
233 |
/**
|
|
234 |
* Get the debugging type.
|
|
235 |
*
|
|
236 |
* @return sum of current debugging types.
|
|
237 |
*/
|
|
238 | 0 |
public static int getDebugType() { |
239 | 0 |
return s_debugType;
|
240 |
} |
|
241 |
|
|
242 |
/**
|
|
243 |
* Returns a singleton instance of this class. This
|
|
244 |
* method should be used instead of the constructor.
|
|
245 |
* to attempt to consolidate events.
|
|
246 |
*
|
|
247 |
* @return JFCEventManager singleton instance.
|
|
248 |
*/
|
|
249 | 730 |
public static JFCEventManager getEventManager() { |
250 | 730 |
return JFCEventManager.getEventManager(DEFAULT_HOLDTIME);
|
251 |
} |
|
252 |
|
|
253 |
/**
|
|
254 |
* Returns a singleton instance of this class. This
|
|
255 |
* method should be used instead of the constructor.
|
|
256 |
*
|
|
257 |
* @param holdTime druration a event should be held
|
|
258 |
* to attempt to consolidate events.
|
|
259 |
*
|
|
260 |
* @return JFCEventManager singleton instance.
|
|
261 |
*/
|
|
262 | 730 |
public static JFCEventManager getEventManager(final long holdTime) { |
263 | 730 |
if (s_singleton == null) { |
264 | 4 |
s_singleton = new JFCEventManager(holdTime);
|
265 |
} |
|
266 |
|
|
267 | 730 |
return s_singleton;
|
268 |
} |
|
269 |
|
|
270 |
/**
|
|
271 |
* Set the recording state.
|
|
272 |
*
|
|
273 |
* @param recording true if enabled. Otherwise false.
|
|
274 |
*/
|
|
275 | 190 |
public static void setRecording(final boolean recording) { |
276 | 190 |
getEventManager().setRecordingImpl(recording); |
277 |
} |
|
278 |
|
|
279 |
/**
|
|
280 |
* Set the maximum hold time for a event.
|
|
281 |
*
|
|
282 |
* @param holdTime maximum duration in millis to
|
|
283 |
* hold a event.
|
|
284 |
*/
|
|
285 | 4 |
public void setHoldTime(final long holdTime) { |
286 | 4 |
m_holdTime = holdTime; |
287 |
} |
|
288 |
|
|
289 |
/**
|
|
290 |
* Get the maximum hold time for a event.
|
|
291 |
*
|
|
292 |
* @return long maximum hold time.
|
|
293 |
*/
|
|
294 | 160 |
public long getHoldTime() { |
295 | 160 |
return m_holdTime;
|
296 |
} |
|
297 |
|
|
298 |
/**
|
|
299 |
* Get the current recording state.
|
|
300 |
*
|
|
301 |
* @return boolean recording state. True if recording is
|
|
302 |
* enabled.
|
|
303 |
* Otherwise, false.
|
|
304 |
*/
|
|
305 | 0 |
public boolean getRecording() { |
306 | 0 |
return m_recording;
|
307 |
} |
|
308 |
|
|
309 |
/**
|
|
310 |
* Add a listener.
|
|
311 |
*
|
|
312 |
* @param jl Listener to be added.
|
|
313 |
*/
|
|
314 | 43 |
public void addJFCEventDataListener(final JFCEventDataListener jl) { |
315 | 43 |
m_listenerList.add(JFCEventDataListener.class, jl);
|
316 |
} |
|
317 |
|
|
318 |
/**
|
|
319 |
* Converts the event to a drag event if necessary.
|
|
320 |
*
|
|
321 |
* @param ae Event to be processed.
|
|
322 |
* @return true if converted to drag event.
|
|
323 |
*/
|
|
324 | 163 |
public boolean convertDrag(final AWTEvent ae) { |
325 | 163 |
if (!(m_pendingEvent instanceof AbstractMouseEventData) |
326 |
|| !(ae instanceof MouseEvent)) {
|
|
327 | 107 |
return false; |
328 |
} |
|
329 |
|
|
330 | 56 |
MouseEvent me = (MouseEvent) ae; |
331 |
|
|
332 | 56 |
if (me.getID() != MouseEvent.MOUSE_DRAGGED) {
|
333 | 29 |
return false; |
334 |
} |
|
335 |
|
|
336 | 27 |
m_pendingEvent = new DragEventData((JFCTestCase) null, |
337 |
(AbstractMouseEventData) m_pendingEvent); |
|
338 |
|
|
339 | 27 |
return true; |
340 |
} |
|
341 |
|
|
342 |
/**
|
|
343 |
* Create a event for the data given.
|
|
344 |
*
|
|
345 |
* @param ae Event to be processed.
|
|
346 |
* @return true if the event is created.
|
|
347 |
*/
|
|
348 | 460 |
public AbstractEventData createEvent(final AWTEvent ae) {
|
349 | 460 |
Object source = ae.getSource(); |
350 |
|
|
351 | 460 |
ArrayList mapping = (ArrayList) UIManager.get(EVENT_MAPPING_PROPERTY); |
352 | 460 |
Iterator iter = mapping.iterator(); |
353 |
|
|
354 | 460 |
while (iter.hasNext()) {
|
355 | 4153 |
String[] map = (String[]) iter.next(); |
356 | 4153 |
Class target = null;
|
357 |
|
|
358 | 4153 |
try {
|
359 | 4153 |
if (!map[1].equals("*")) { |
360 | 2867 |
target = Class.forName(map[1]); |
361 |
} else {
|
|
362 | 1286 |
target = Object.class;
|
363 |
} |
|
364 |
} catch (Exception e) {
|
|
365 |
// don't do anything, but still catch it
|
|
366 |
} |
|
367 |
|
|
368 | 4153 |
if (target.isInstance(source)) {
|
369 |
// Attempt to consume event.
|
|
370 | 1541 |
try {
|
371 | 1541 |
if (s_debug && ((s_debugType & DEBUG_CREATE) > 0)) {
|
372 | 74 |
System.err.println( |
373 |
"JFCEventManager.createEvent check class:" + map[0]
|
|
374 |
+ " can handle " + ae);
|
|
375 |
} |
|
376 |
|
|
377 | 1541 |
Class cls = Class.forName(map[0]); |
378 | 1541 |
AbstractEventData data = (AbstractEventData) cls |
379 |
.newInstance(); |
|
380 |
|
|
381 | 1541 |
if (data.canConsume(ae)) {
|
382 | 360 |
return data;
|
383 |
} |
|
384 |
} catch (Exception e) {
|
|
385 | 0 |
System.err.println( |
386 |
"Exception attempting to create instnce for:" + map[0]);
|
|
387 | 0 |
e.printStackTrace(); |
388 |
} |
|
389 |
} |
|
390 |
} |
|
391 |
|
|
392 | 100 |
if (s_debug && ((s_debugType & DEBUG_CREATE) > 0)) {
|
393 | 4 |
System.err.println( |
394 |
"JFCEventManager.createEvent No EventData structure for:" + ae);
|
|
395 |
} |
|
396 |
|
|
397 | 100 |
return null; |
398 |
} |
|
399 |
|
|
400 |
/**
|
|
401 |
* This method implements the {@link AWTEventListener} interface.
|
|
402 |
* This method will be accessed for every {@link AWTEvent} which is
|
|
403 |
* of the type: MOUSE_MOTION_EVENT, MOUSE_EVENT, KEY_EVENT, or
|
|
404 |
* TEXT_EVENT.
|
|
405 |
*
|
|
406 |
* @param ae Event to be processed.
|
|
407 |
*/
|
|
408 | 7508 |
public void eventDispatched(final AWTEvent ae) { |
409 | 7508 |
processEventData(ae); |
410 |
} |
|
411 |
|
|
412 |
/**
|
|
413 |
* Remove all listeners.
|
|
414 |
*/
|
|
415 | 120 |
public void removeAllJFCEventDataListeners() { |
416 | 120 |
Object[] listeners = m_listenerList.getListenerList(); |
417 |
|
|
418 | 120 |
for (int i = 0; i < listeners.length; i += 2) { |
419 | 17 |
if (listeners[i] == JFCEventDataListener.class) { |
420 | 17 |
m_listenerList.remove(JFCEventDataListener.class,
|
421 |
(EventListener) listeners[i + 1]); |
|
422 |
} |
|
423 |
} |
|
424 |
} |
|
425 |
|
|
426 |
/**
|
|
427 |
* Remove a listener.
|
|
428 |
*
|
|
429 |
* @param jl Listener to be removed.
|
|
430 |
*/
|
|
431 | 26 |
public void removeJFCEventDataListener(final JFCEventDataListener jl) { |
432 | 26 |
m_listenerList.remove(JFCEventDataListener.class, jl);
|
433 |
} |
|
434 |
|
|
435 |
/**
|
|
436 |
* Fire event data to the listeners.
|
|
437 |
*/
|
|
438 | 360 |
protected void fireEventData() { |
439 | 360 |
if ((m_pendingEvent != null) && m_pendingEvent.isValid()) { |
440 | 254 |
if (s_debug && ((s_debugType & DEBUG_OUTPUT) > 0)) {
|
441 | 9 |
System.err.println("JFCEventManager.outputEvent:"
|
442 |
+ m_pendingEvent); |
|
443 |
} |
|
444 |
|
|
445 | 254 |
Object[] listeners = m_listenerList.getListenerList(); |
446 |
|
|
447 | 254 |
for (int i = listeners.length - 2; i >= 0; i -= 2) { |
448 | 249 |
if (listeners[i] == JFCEventDataListener.class) { |
449 | 249 |
((JFCEventDataListener) listeners[i + 1]).handleEvent(m_pendingEvent); |
450 |
} |
|
451 |
} |
|
452 |
} |
|
453 |
|
|
454 | 360 |
m_pendingEvent = null;
|
455 |
} |
|
456 |
|
|
457 |
/**
|
|
458 |
* This method converts the {@link AWTEvent} to the corresponding
|
|
459 |
* AbstractEventData.
|
|
460 |
*
|
|
461 |
* @param ae AWTEvent to be processed.
|
|
462 |
*/
|
|
463 | 7508 |
protected synchronized void processEventData(final AWTEvent ae) { |
464 | 7508 |
if (s_debug && ((s_debugType & DEBUG_INPUT) > 0)) {
|
465 | 226 |
System.err.println("JFCEventManager.inputEvent:"
|
466 |
+ ((InputEvent) ae).getWhen() + " " + ae);
|
|
467 |
} |
|
468 |
|
|
469 | 7508 |
m_lastEventTime = System.currentTimeMillis(); |
470 |
|
|
471 | 7508 |
if (ae instanceof MouseEvent |
472 |
&& (((MouseEvent) ae).getID() == MouseEvent.MOUSE_MOVED)) { |
|
473 | 4503 |
return;
|
474 |
} |
|
475 |
|
|
476 | 3005 |
if ((m_pendingEvent != null) && !m_pendingEvent.canConsume(ae)) { |
477 | 163 |
if (!convertDrag(ae)) {
|
478 | 136 |
fireEventData(); |
479 |
} |
|
480 |
} |
|
481 |
|
|
482 | 3005 |
if (m_pendingEvent == null) { |
483 | 460 |
m_pendingEvent = createEvent(ae); |
484 |
} |
|
485 |
|
|
486 | 3005 |
if (m_pendingEvent != null) { |
487 | 2905 |
m_pendingEvent.consume(ae); |
488 | 2905 |
m_lastEventTime = 0; |
489 |
} |
|
490 |
} |
|
491 |
|
|
492 |
/**
|
|
493 |
* Set the recording state.
|
|
494 |
*
|
|
495 |
* @param recording true if recording is to be enabled.
|
|
496 |
* otherwise false.
|
|
497 |
*/
|
|
498 | 190 |
private void setRecordingImpl(final boolean recording) { |
499 | 190 |
if (recording && !(this.m_recording)) { |
500 | 43 |
getDebug(); |
501 |
|
|
502 | 43 |
long wheelMask = 0;
|
503 |
|
|
504 | 43 |
try {
|
505 | 43 |
wheelMask = AWTEvent.class.getField("MOUSE_WHEEL_EVENT_MASK") |
506 |
.getLong(null);
|
|
507 |
} catch (Exception e) {
|
|
508 | 0 |
wheelMask = 0; |
509 |
} |
|
510 |
|
|
511 | 43 |
Toolkit.getDefaultToolkit().addAWTEventListener(this,
|
512 |
AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK |
|
513 |
| AWTEvent.KEY_EVENT_MASK | AWTEvent.TEXT_EVENT_MASK |
|
514 |
| wheelMask); |
|
515 | 43 |
m_timerThread = new Thread(
|
516 |
new Runnable() {
|
|
517 | 43 |
public void run() { |
518 | 43 |
while (m_recording) {
|
519 | 2243 |
try {
|
520 | 2243 |
Thread.currentThread().sleep(m_holdTime); |
521 |
} catch (InterruptedException ie) {
|
|
522 |
// don't do anything, but still catch it
|
|
523 |
} |
|
524 |
|
|
525 | 2242 |
synchronized (JFCEventManager.this) { |
526 | 2242 |
long time = System.currentTimeMillis();
|
527 |
|
|
528 | 2242 |
if (m_lastEventTime == 0) {
|
529 | 309 |
m_lastEventTime = System |
530 |
.currentTimeMillis(); |
|
531 |
} |
|
532 |
|
|
533 | 2242 |
if ((m_pendingEvent != null) |
534 |
&& ((time - m_lastEventTime) > m_holdTime)) { |
|
535 | 224 |
fireEventData(); |
536 |
} |
|
537 |
} |
|
538 |
} |
|
539 |
|
|
540 | 42 |
if (m_pendingEvent != null) { |
541 | 0 |
fireEventData(); |
542 |
} |
|
543 |
} |
|
544 |
}, |
|
545 |
"JFCEventManager Timer");
|
|
546 |
|
|
547 | 43 |
m_timerThread.start(); |
548 | 43 |
this.m_recording = recording;
|
549 | 147 |
} else if (!recording && (this.m_recording)) { |
550 | 42 |
Toolkit.getDefaultToolkit().removeAWTEventListener(this);
|
551 | 42 |
this.m_recording = recording;
|
552 | 42 |
m_timerThread.interrupt(); |
553 |
|
|
554 |
// try {
|
|
555 |
// timerThread.join();
|
|
556 |
// } catch (InterruptedException ie) {
|
|
557 |
// // don't do anything, but still catch it
|
|
558 |
// }
|
|
559 |
} |
|
560 |
|
|
561 | 190 |
this.m_recording = recording;
|
562 |
} |
|
563 |
} |
|
564 |
|
|