001 /* 002 * Created on Jul 19, 2008 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 005 * the License. You may obtain a copy of the License at 006 * 007 * http://www.apache.org/licenses/LICENSE-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 011 * specific language governing permissions and limitations under the License. 012 * 013 * Copyright @2008-2010 the original author or authors. 014 */ 015 package org.fest.swing.core; 016 017 import static java.awt.AWTEvent.KEY_EVENT_MASK; 018 import static java.awt.event.InputEvent.CTRL_MASK; 019 import static java.awt.event.InputEvent.SHIFT_MASK; 020 import static java.awt.event.KeyEvent.KEY_PRESSED; 021 import static java.awt.event.KeyEvent.VK_A; 022 import static org.fest.swing.core.InputModifiers.modifiersMatch; 023 import static org.fest.swing.core.InputModifiers.unify; 024 025 import java.awt.AWTEvent; 026 import java.awt.Toolkit; 027 import java.awt.event.AWTEventListener; 028 import java.awt.event.KeyEvent; 029 030 import org.fest.util.VisibleForTesting; 031 032 /** 033 * Understands an escape valve for users to abort a running FEST-Swing test by pressing 'Ctrl + Shift + A'. The key 034 * combination to use to abort test is configurable through the method 035 * <code>{@link EmergencyAbortListener#keyCombination(KeyPressInfo)}</code>. 036 * <p> 037 * The following example shows to use this listener in a TestNG test: 038 * <pre> 039 * private EmergencyAbortListener listener; 040 * 041 * 042 * @BeforeMethod public void setUp() { 043 * // set up your test fixture. 044 * listener = EmergencyAbortListener.registerInToolkit(); 045 * } 046 * 047 * @AfterMethod public void tearDown() { 048 * // clean up resources. 049 * listener.unregister(); 050 * } 051 * </pre> 052 * </p> 053 * <p> 054 * Changing the default key combination for aborting test: 055 * <pre> 056 * listener = EmergencyAbortListener.registerInToolkit().{@link EmergencyAbortListener#keyCombination(KeyPressInfo) keyCombination}(key(VK_C).modifiers(SHIFT_MASK)); 057 * </pre> 058 * </p> 059 * 060 * @author <a href="mailto:simeon.fitch@mseedsoft.com">Simeon H.K. Fitch</a> 061 * @author Alex Ruiz 062 */ 063 public class EmergencyAbortListener implements AWTEventListener { 064 065 private static final long EVENT_MASK = KEY_EVENT_MASK; 066 067 private final Toolkit toolkit; 068 private final TestTerminator testTerminator; 069 070 private int keyCode = VK_A; 071 private int modifiers = unify(CTRL_MASK, SHIFT_MASK); 072 073 /** 074 * Attaches a new instance of <code>{@link EmergencyAbortListener}</code> in the given <code>{@link Toolkit}</code>. 075 * Any other instances of <code>EmergencyAbortListener</code> will be removed from the <code>Toolkit</code>. 076 * @return the created listener. 077 */ 078 public static EmergencyAbortListener registerInToolkit() { 079 EmergencyAbortListener listener = new EmergencyAbortListener(Toolkit.getDefaultToolkit()); 080 listener.register(); 081 return listener; 082 } 083 084 @VisibleForTesting 085 EmergencyAbortListener(Toolkit toolkit) { 086 this(toolkit, new TestTerminator()); 087 } 088 089 @VisibleForTesting 090 EmergencyAbortListener(Toolkit toolkit, TestTerminator testTerminator) { 091 this.testTerminator = testTerminator; 092 this.toolkit = toolkit; 093 } 094 095 @VisibleForTesting 096 void register() { 097 removePrevious(); 098 toolkit.addAWTEventListener(this, EVENT_MASK); 099 } 100 101 private void removePrevious() { 102 AWTEventListener[] listeners = toolkit.getAWTEventListeners(EVENT_MASK); 103 for (AWTEventListener listener : listeners) 104 if (listener instanceof EmergencyAbortListener) toolkit.removeAWTEventListener(listener); 105 } 106 107 /** 108 * Sets the key combination that will terminate any FEST-Swing test. The default combination is 'Ctrl + Shift + A'. 109 * @param keyPressInfo contains information about the key code and modifiers. 110 * @return this listener. 111 * @throws NullPointerException if the <code>KeyPressInfo</code> is <code>null</code>. 112 */ 113 public EmergencyAbortListener keyCombination(KeyPressInfo keyPressInfo) { 114 if (keyPressInfo == null) throw new NullPointerException("KeyPressInfo should not be null"); 115 keyCode = keyPressInfo.keyCode(); 116 modifiers = unify(keyPressInfo.modifiers()); 117 return this; 118 } 119 120 /** 121 * Removes this listener from the <code>{@link Toolkit}</code> this listener is attached to. 122 */ 123 public void unregister() { 124 toolkit.removeAWTEventListener(this); 125 } 126 127 /** 128 * Inspects key events for the key combination that should terminate any running FEST-Swing tests. 129 * @param event the event to inspect. 130 * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent) 131 */ 132 public void eventDispatched(AWTEvent event) { 133 if (event.getID() != KEY_PRESSED) return; 134 if (!(event instanceof KeyEvent)) return; 135 KeyEvent e = (KeyEvent) event; 136 if (e.getKeyCode() != keyCode) return; 137 if (!modifiersMatch(e, modifiers)) return; 138 testTerminator.terminateTests(); 139 } 140 141 @VisibleForTesting 142 int keyCode() { return keyCode; } 143 144 @VisibleForTesting 145 int modifiers() { return modifiers; } 146 }