package com.google.code.joto.eventrecorder.spy.awtspy; import java.awt.AWTEvent; import java.awt.Component; import java.awt.Container; import java.awt.event.AWTEventListener; import java.awt.event.ActionEvent; import java.awt.event.AdjustmentEvent; import java.awt.event.ComponentEvent; import java.awt.event.ContainerEvent; import java.awt.event.FocusEvent; import java.awt.event.HierarchyEvent; import java.awt.event.InputMethodEvent; import java.awt.event.InvocationEvent; import java.awt.event.ItemEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.TextEvent; import java.awt.event.WindowEvent; import java.io.Serializable; import java.util.Date; import java.util.HashMap; import java.util.Map; import javax.swing.JComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.code.joto.eventrecorder.RecordEventSummary; import com.google.code.joto.eventrecorder.spy.awtspy.AWTEventInfos.AWTEventGroupInfo; import com.google.code.joto.eventrecorder.spy.awtspy.AWTEventInfos.AWTEventInfo; import com.google.code.joto.eventrecorder.spy.awtspy.AWTEventInfos.AWTEventMaskInfo; import com.google.code.joto.eventrecorder.writer.RecordEventWriter; /** * a spy to listen AWT event, and write them as RecordEventSummary * into a RecordEventWriter * */ public class AWTRecordEventWriterSpy { private static Logger log = LoggerFactory.getLogger(AWTRecordEventWriterSpy.class); public static final String MARKER_KEY_IGNORE_AWTEVENT_SPY = "MARKER_KEY_IGNORE_AWTEVENT_SPY"; protected RecordEventWriter eventWriter; protected boolean enable = false; protected AWTEventListener innerAWTEventListener; protected long awtEventMask = 0; // sub-events enable/disable flags, per group of eventTypes (or per eventIDs) private static class AWTEventGroupFlag { private boolean enable = true; } private Map<AWTEventGroupInfo,AWTEventGroupFlag> eventGroupFlags = new HashMap<AWTEventGroupInfo,AWTEventGroupFlag>(); // ------------------------------------------------------------------------ public AWTRecordEventWriterSpy(RecordEventWriter eventWriter) { this.eventWriter = eventWriter; this.innerAWTEventListener = new AWTEventListener() { @Override public void eventDispatched(AWTEvent event) { onAWTEventDispatched(event); } }; enable = false; for (AWTEventMaskInfo maskInfo : AWTEventMaskInfo.values()) { setAwtEventMaskFlag(maskInfo, maskInfo.getDefaultSelected()); for (AWTEventGroupInfo eventGroup : maskInfo.getEventGroups()) { setEventGroupFlag(eventGroup, eventGroup.getDefaultSelected()); } } } // ------------------------------------------------------------------------ public static void setIgnoreComponentAwtEventSpy(JComponent comp) { comp.putClientProperty(MARKER_KEY_IGNORE_AWTEVENT_SPY, Boolean.TRUE); } public static void clearIgnoreComponentAwtEventSpy(JComponent comp) { comp.putClientProperty(MARKER_KEY_IGNORE_AWTEVENT_SPY, null); } public static boolean isSetIgnoreComponentAwtEventSpy(Component comp) { if (comp instanceof JComponent) { return ((JComponent) comp).getClientProperty(MARKER_KEY_IGNORE_AWTEVENT_SPY) != null; } else { return false; //?? no marker on old AWT comp? } } public static boolean isIgnoreComponentAwtEventSpyHierarchy(Component comp) { boolean res = false; if (isSetIgnoreComponentAwtEventSpy(comp)) { res = true; } else { // also find marker in ancestor hierarchy for(Component p = comp; p != null; p = p.getParent()) { if (isSetIgnoreComponentAwtEventSpy(p)) { res = true; break; } } } return res; } public boolean isEnable() { return enable; } public void setEnable(boolean p) { if (p != enable) { if (enable && (awtEventMask != 0)) { uninstallAWTEventListener(); } this.enable = p; if (enable && (awtEventMask != 0)) { installAWTEventListener(); } } } public long getAwtEventMask() { return awtEventMask; } public void setAwtEventMask(long p) { if (p != awtEventMask) { if (enable && (awtEventMask != 0)) { uninstallAWTEventListener(); } this.awtEventMask = p; if (enable && (awtEventMask != 0)) { installAWTEventListener(); } } } public boolean getAwtEventMaskFlag(AWTEventMaskInfo awtEventMaskInfo) { return 0 != (awtEventMask & awtEventMaskInfo.getFlag()); } public void setAwtEventMaskFlag(AWTEventMaskInfo awtEventMaskInfo, boolean p) { long newMask = clearOrSetFlag(awtEventMask, awtEventMaskInfo.getFlag(), p); setAwtEventMask(newMask); } public static long clearOrSetFlag(long mask, long flag, boolean isSetFlag) { long res = (isSetFlag)? (mask | flag) : (mask & (~flag)); return res; } public static String maskToString(long mask) { StringBuilder sb = new StringBuilder(); for (AWTEventMaskInfo flagInfo : AWTEventMaskInfo.values()) { if (0 != (mask & flagInfo.getFlag())) { sb.append(flagInfo.getFlagName() + " |"); } } if (sb.length() != 0) { sb.delete(sb.length()-2, sb.length()); } return sb.toString(); } public boolean getEventGroupFlag(AWTEventGroupInfo key) { AWTEventGroupFlag tmp = getOrCreateEventGroupFlag(key); return tmp.enable; } public void setEventGroupFlag(AWTEventGroupInfo key, boolean enable) { AWTEventGroupFlag tmp = getOrCreateEventGroupFlag(key); tmp.enable = enable; } private AWTEventGroupFlag getOrCreateEventGroupFlag(AWTEventGroupInfo key) { AWTEventGroupFlag tmp = eventGroupFlags.get(key); if (tmp == null) { tmp = new AWTEventGroupFlag(); tmp.enable = key.getDefaultSelected(); eventGroupFlags.put(key, tmp); } return tmp; } private void installAWTEventListener() { log.info("add AWTEventListener for joto event writer: mask=" + awtEventMask + " (" + maskToString(awtEventMask) + ")"); try { java.awt.Toolkit.getDefaultToolkit().addAWTEventListener( innerAWTEventListener, awtEventMask); } catch (SecurityException ex) { log.error("Failed to add AWTEventListener ... ignore!", ex); } } private void uninstallAWTEventListener() { log.info("remove AWTEventListener for joto event writer"); java.awt.Toolkit.getDefaultToolkit().removeAWTEventListener( innerAWTEventListener); } private void onAWTEventDispatched(AWTEvent event) { if (!enable) { return; } Object eventSource = event.getSource(); if (eventSource != null && (eventSource instanceof Component)) { if (isIgnoreComponentAwtEventSpyHierarchy((Component) eventSource)) { return; } } AWTEventInfo eventInfo; String eventMethodDetail = null; Serializable eventData = null; switch (event.getID()) { // ComponentEvent case ComponentEvent.COMPONENT_SHOWN: eventInfo = AWTEventInfo.COMPONENT_SHOWN; break; case ComponentEvent.COMPONENT_HIDDEN: eventInfo = AWTEventInfo.COMPONENT_HIDDEN; break; case ComponentEvent.COMPONENT_MOVED: eventInfo = AWTEventInfo.COMPONENT_MOVED; break; case ComponentEvent.COMPONENT_RESIZED: eventInfo = AWTEventInfo.COMPONENT_RESIZED; break; // ContainerEvent case ContainerEvent.COMPONENT_ADDED: eventInfo = AWTEventInfo.COMPONENT_ADDED; break; case ContainerEvent.COMPONENT_REMOVED: eventInfo = AWTEventInfo.COMPONENT_REMOVED; break; // FocusEvent case FocusEvent.FOCUS_GAINED: eventInfo = AWTEventInfo.FOCUS_GAINED; // eventMethodDetail = (event.temporary ? ",temporary" : ",permanent") + ",opposite=" + event.getOppositeComponent(); break; case FocusEvent.FOCUS_LOST: eventInfo = AWTEventInfo.FOCUS_LOST; // eventMethodDetail = (event.temporary ? ",temporary" : ",permanent") + ",opposite=" + event.getOppositeComponent(); break; // KeyEvent case KeyEvent.KEY_PRESSED: case KeyEvent.KEY_RELEASED: case KeyEvent.KEY_TYPED: { KeyEvent e = (KeyEvent) event; switch(event.getID()) { case KeyEvent.KEY_PRESSED: { eventInfo = AWTEventInfo.KEY_PRESSED; } break; case KeyEvent.KEY_RELEASED: { eventInfo = AWTEventInfo.KEY_RELEASED; } break; case KeyEvent.KEY_TYPED: { eventInfo = AWTEventInfo.KEY_TYPED; } break; default: eventInfo = null; // for compiler, can not occur } eventMethodDetail = ""; if (e.getModifiers() != 0) { eventMethodDetail += KeyEvent.getKeyModifiersText(e.getModifiers()) + " "; } if (e.getModifiersEx() != 0) { eventMethodDetail += KeyEvent.getModifiersExText(e.getModifiers()) + " "; } String keyCharStr = keyCharToString(e.getKeyChar()); if (keyCharStr != null) { eventMethodDetail += keyCharStr; } if (e.getKeyCode() != 0) { eventMethodDetail += " (keyCode=" + Integer.toString(e.getKeyCode()) + ")"; } String keyText = KeyEvent.getKeyText(e.getKeyCode()); if (keyText.indexOf("unknown") != -1) { eventMethodDetail += " " + keyText; } } break; // MouseEvent case MouseEvent.MOUSE_PRESSED: eventInfo = AWTEventInfo.MOUSE_PRESSED; break; case MouseEvent.MOUSE_RELEASED: eventInfo = AWTEventInfo.MOUSE_RELEASED; break; case MouseEvent.MOUSE_CLICKED: eventInfo = AWTEventInfo.MOUSE_CLICKED; break; case MouseEvent.MOUSE_MOVED: eventInfo = AWTEventInfo.MOUSE_MOVED; break; case MouseEvent.MOUSE_DRAGGED: eventInfo = AWTEventInfo.MOUSE_DRAGGED; break; case MouseEvent.MOUSE_ENTERED: eventInfo = AWTEventInfo.MOUSE_ENTERED; break; case MouseEvent.MOUSE_EXITED: eventInfo = AWTEventInfo.MOUSE_EXITED; break; case MouseEvent.MOUSE_WHEEL: // cf also MouseWheelEvent eventInfo = AWTEventInfo.MOUSE_WHEEL; break; // WindowEvent case WindowEvent.WINDOW_OPENED: eventInfo = AWTEventInfo.WINDOW_OPENED; break; case WindowEvent.WINDOW_CLOSING: eventInfo = AWTEventInfo.WINDOW_CLOSING; break; case WindowEvent.WINDOW_CLOSED: eventInfo = AWTEventInfo.WINDOW_CLOSED; break; case WindowEvent.WINDOW_ICONIFIED: eventInfo = AWTEventInfo.WINDOW_ICONIFIED; break; case WindowEvent.WINDOW_DEICONIFIED: eventInfo = AWTEventInfo.WINDOW_DEICONIFIED; break; case WindowEvent.WINDOW_ACTIVATED: eventInfo = AWTEventInfo.WINDOW_ACTIVATED; break; case WindowEvent.WINDOW_DEACTIVATED: eventInfo = AWTEventInfo.WINDOW_DEACTIVATED; break; case WindowEvent.WINDOW_GAINED_FOCUS: eventInfo = AWTEventInfo.WINDOW_GAINED_FOCUS; break; case WindowEvent.WINDOW_LOST_FOCUS: eventInfo = AWTEventInfo.WINDOW_LOST_FOCUS; break; case WindowEvent.WINDOW_STATE_CHANGED: eventInfo = AWTEventInfo.WINDOW_STATE_CHANGED; break; // ActionEvent case ActionEvent.ACTION_PERFORMED: eventInfo = AWTEventInfo.ACTION_PERFORMED; break; // AdjustmentEvent case AdjustmentEvent.ADJUSTMENT_VALUE_CHANGED: { eventInfo = AWTEventInfo.ADJUSTMENT_VALUE_CHANGED; AdjustmentEvent e = (AdjustmentEvent) event; String adjTypeStr; switch(e.getAdjustmentType()) { case AdjustmentEvent.UNIT_INCREMENT: adjTypeStr = "UNIT_INCREMENT"; break; case AdjustmentEvent.UNIT_DECREMENT: adjTypeStr = "UNIT_DECREMENT"; break; case AdjustmentEvent.BLOCK_INCREMENT: adjTypeStr = "BLOCK_INCREMENT"; break; case AdjustmentEvent.BLOCK_DECREMENT: adjTypeStr = "BLOCK_DECREMENT"; break; case AdjustmentEvent.TRACK: adjTypeStr = "TRACK"; break; default: adjTypeStr = "unknown type"; } eventMethodDetail = "adjType="+adjTypeStr + ",value=" + e.getValue() + ",isAdjusting="+e.getValueIsAdjusting(); } break; // ItemEvent case ItemEvent.ITEM_STATE_CHANGED: { eventInfo = AWTEventInfo.ITEM_STATE_CHANGED; ItemEvent e = (ItemEvent) event; String stateStr; switch(e.getStateChange()) { case ItemEvent.SELECTED: stateStr = "SELECTED"; break; case ItemEvent.DESELECTED: stateStr = "DESELECTED"; break; default: stateStr = "unknown type"; } eventMethodDetail = "item="+e.getItem() + ",stateChange="+stateStr; } break; // TextEvent case TextEvent.TEXT_VALUE_CHANGED: { eventInfo = AWTEventInfo.TEXT_VALUE_CHANGED; // TextEvent e = (TextEvent) event; // eventMethodDetail = e.paramString(); } break; // InputMethodEvent case InputMethodEvent.INPUT_METHOD_TEXT_CHANGED: { eventInfo = AWTEventInfo.INPUT_METHOD_TEXT_CHANGED; InputMethodEvent e = (InputMethodEvent) event; eventMethodDetail = e.paramString(); } break; case InputMethodEvent.CARET_POSITION_CHANGED: { eventInfo = AWTEventInfo.CARET_POSITION_CHANGED; InputMethodEvent e = (InputMethodEvent) event; eventMethodDetail = e.paramString(); } break; // PaintEvent ?? // InvocationEvent, INVOCATION_EVENT_MASK case InvocationEvent.INVOCATION_DEFAULT: { eventInfo = AWTEventInfo.INVOCATION_DEFAULT; InvocationEvent e = (InvocationEvent) event; eventMethodDetail = e.paramString(); // = typeStr + ",runnable=" + runnable + ",notifier=" + notifier + ",catchExceptions=" + catchExceptions + ",when=" + when; } break; // HierarchyEvent, HIERARCHY_EVENT_MASK case HierarchyEvent.HIERARCHY_CHANGED: case HierarchyEvent.ANCESTOR_MOVED: case HierarchyEvent.ANCESTOR_RESIZED: { HierarchyEvent e = (HierarchyEvent) event; Container changedParent = e.getChangedParent(); if (isIgnoreComponentAwtEventSpyHierarchy(changedParent)) { return; } Component changedComp = e.getComponent(); if (isIgnoreComponentAwtEventSpyHierarchy(changedComp)) { return; } switch(event.getID()) { case HierarchyEvent.ANCESTOR_MOVED: eventInfo = AWTEventInfo.ANCESTOR_MOVED; // "("+changed+","+changedParent+")"; break; case HierarchyEvent.HIERARCHY_CHANGED: eventInfo = AWTEventInfo.HIERARCHY_CHANGED; break; case HierarchyEvent.ANCESTOR_RESIZED: eventInfo = AWTEventInfo.ANCESTOR_RESIZED; // ("+changed+","+changedParent+")"; break; default: eventInfo = null; // can not occur } } break; // HIERARCHY_BOUNDS_EVENT_MASK ?? // MouseWheelEvent, MOUSE_WHEEL_EVENT_MASK ... cf MouseEvent // WINDOW_STATE_EVENT_MASK ... cf WindowEvent // WINDOW_FOCUS_EVENT_MASK .. cf WindowEvent default: eventInfo = AWTEventInfo.UNKNOWN_EVENT; } AWTEventGroupInfo eventGroupInfo = (eventInfo != null)? eventInfo.getEventGroup() : AWTEventGroupInfo.UNKNOWN; if (eventGroupInfo == null) { // SHOULD NOT OCCUR eventGroupInfo = AWTEventGroupInfo.UNKNOWN; } boolean enableEvent = getEventGroupFlag(eventGroupInfo); if (!enableEvent) { return; } if (enableEvent) { String typeStr; String eventClassName; String eventMethodName; if (eventInfo != AWTEventInfo.UNKNOWN_EVENT) { typeStr = eventInfo.getEventName(); eventClassName = eventInfo.getEventClassName(); eventMethodName = eventInfo.getEventMethodName(); } else { enableEvent = true; typeStr = "ID:" + event.getID(); eventClassName = null; eventMethodName = null; } if (eventMethodDetail == null) { eventMethodDetail = event.paramString(); // default built-in <<toString>> from AWT } RecordEventSummary recordEvent = new RecordEventSummary(); recordEvent.setEventType("AWTSpy"); recordEvent.setEventSubType(typeStr); recordEvent.setEventDate(new Date()); recordEvent.setThreadName(Thread.currentThread().getName()); // should be the EDT ... recordEvent.setEventClassName(eventClassName); recordEvent.setEventMethodName(eventMethodName); recordEvent.setEventMethodDetail(eventMethodDetail); try { eventWriter.addEvent(recordEvent, eventData, null); } catch(Exception ex) { log.warn("should not occur.. ignore", ex); } } } private static String keyCharToString(char keyChar) { String res; switch (keyChar) { case '\b': res = KeyEvent.getKeyText(KeyEvent.VK_BACK_SPACE); break; case '\t': res = KeyEvent.getKeyText(KeyEvent.VK_TAB); break; case '\n': res = KeyEvent.getKeyText(KeyEvent.VK_ENTER); break; case '\u0018': res = KeyEvent.getKeyText(KeyEvent.VK_CANCEL); break; case '\u001b': res = KeyEvent.getKeyText(KeyEvent.VK_ESCAPE); break; case '\u007f': res = KeyEvent.getKeyText(KeyEvent.VK_DELETE); break; case KeyEvent.CHAR_UNDEFINED: res = null; break; // undefined... default: res = Character.toString(keyChar); break; } return res; } }