Source Code Cross Referenced for ComponentRecorder.java in  » Testing » abbot-1.0.1 » abbot » editor » recorder » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Testing » abbot 1.0.1 » abbot.editor.recorder 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        package abbot.editor.recorder;
0002:
0003:        import java.awt.*;
0004:        import java.awt.event.*;
0005:        import java.text.*;
0006:        import java.util.*;
0007:
0008:        import javax.swing.*;
0009:        import javax.swing.text.JTextComponent;
0010:
0011:        import abbot.BugReport;
0012:        import abbot.Log;
0013:        import abbot.Platform;
0014:
0015:        import abbot.finder.matchers.JMenuItemMatcher;
0016:
0017:        import abbot.i18n.Strings;
0018:        import abbot.script.*;
0019:        import abbot.script.Action;
0020:        import abbot.script.Resolver;
0021:        import abbot.tester.*;
0022:        import abbot.tester.Robot;
0023:        import abbot.util.AWT;
0024:
0025:        /**
0026:         * Record basic semantic events you might find on any component.  This class
0027:         * handles the following actions:<p>
0028:         * <ul>
0029:         * <li>window actions
0030:         * <li>popup menus
0031:         * <li>click 
0032:         * <li>typed keys
0033:         * <li>basic drag and drop
0034:         * <li>InputMethod events (extended character input)
0035:         * </ul>
0036:         * Clicks, popup menus, and drag/drop actions may be based on coordinates or
0037:         * component substructure (cell, row, tab, etc) locations.
0038:         *
0039:         * <h3>Window Actions</h3>
0040:         * While these nominally might be handled in a WindowRecorder, they are so
0041:         * common that it's easier to handle here instead.  Currently supports
0042:         * tracking show/hide/activate.  TODO: move/resize/iconfify/deiconify.
0043:         * <h3>Popup Menus</h3>
0044:         * Currently only the click/select/click sequence is supported.  The
0045:         * press/drag/release version shouldn't be hard to implement, though.
0046:         * <h3>Click</h3>
0047:         * Simple press/release on a component, storing the exact coordinate of the
0048:         * click.  Most things with selectability will want to override this.  Culling
0049:         * accidental intervening drags would be nice but probably not worth the
0050:         * effort or complexity (better just to be less sloppy with your mouse).
0051:         * <h3>Key Type</h3>
0052:         * Capture only events that result in actual output.  No plain modifiers,
0053:         * shortcuts, or mnemonics.
0054:         * <h3>Drag/Drop</h3>
0055:         * Basic drag from one component and drop on another, storing exact
0056:         * coordinates of the press/release actions.  Should definitely override this
0057:         * to represent your component's internal objects (e.g. cells in a table).
0058:         * Note that these are two distinct actions, even though they always appear
0059:         * together.  The source is responsible for identifying the drag, and the
0060:         * target is responsible for identifying the drop.
0061:         * <h3>InputMethod</h3>
0062:         * Catch extended character input.
0063:         */
0064:        // NOTE: Mac OSX robot will actually generate key modifiers prior
0065:        // to  button2/3 
0066:        // NOTE: Mac OSX CTRL/ALT+MB1 invokes MB2
0067:        // CTRL+MB1->CTRL+MB2
0068:        // ALT+MB1->MB2
0069:        // TODO: test recorders by sending an event stream; test platform stream
0070:        // by generating robot events and verifying stream seen; this splits the
0071:        // tests into separate concerns.
0072:        public class ComponentRecorder extends SemanticRecorder {
0073:
0074:            private static final String[] TYPES = { "any", "window", "menu",
0075:                    "click", "key", "drag", "drop", "text", "input method" };
0076:
0077:            /** Mappings for special keys. */
0078:            private static java.util.HashMap specialMap;
0079:
0080:            static {
0081:                // Make explicit some special key mappings which we DON'T want to save
0082:                // as the resulting characters (b/c they may not actually be
0083:                // characters, or they're not particularly good to save as
0084:                // characters. 
0085:                int[][] mappings = {
0086:                        { '\t', KeyEvent.VK_TAB },
0087:                        { '', KeyEvent.VK_ESCAPE }, // No escape sequence exists
0088:                        { '\b', KeyEvent.VK_BACK_SPACE },
0089:                        { '', KeyEvent.VK_DELETE }, // No escape sequence exists
0090:                        { '\n', KeyEvent.VK_ENTER },
0091:                        { '\r', KeyEvent.VK_ENTER }, };
0092:                specialMap = new java.util.HashMap();
0093:                for (int i = 0; i < mappings.length; i++) {
0094:                    specialMap.put(String.valueOf((char) mappings[i][0]), AWT
0095:                            .getKeyCode(mappings[i][1]));
0096:                }
0097:            }
0098:
0099:            // For windows
0100:            private Window window = null;
0101:            private boolean isClose = false;
0102:            // For key events
0103:            private char keychar = KeyEvent.CHAR_UNDEFINED;
0104:            private int modifiers;
0105:            // For clicks
0106:            private Component target;
0107:            private Component forwardedTarget;
0108:            private int x, y;
0109:            private boolean released;
0110:            private int clickCount;
0111:            // For menu events
0112:            private Component invoker;
0113:            private int menux, menuy;
0114:            private MenuItem awtMenuTarget;
0115:            private Component menuTarget;
0116:            private boolean isPopup;
0117:            private boolean hasAWTPopup;
0118:            private MenuListener menuListener;
0119:            private boolean menuCanceled;
0120:            // For drag events
0121:            // This class is responsible for handling drag/drop once the action has
0122:            // been recognized by a derived class
0123:            private Component dragSource;
0124:            private int dragx, dragy;
0125:            // For drop events
0126:            private Component dropTarget;
0127:            private int dropx, dropy;
0128:            // InputMethod
0129:            private ArrayList imKeyCodes = new ArrayList();
0130:            private StringBuffer imText = new StringBuffer();
0131:            /** Keep a short-term memory of windows we've seen open/close already. */
0132:            private static WeakHashMap closeEventWindows = new WeakHashMap();
0133:            private static WeakHashMap openEventWindows = new WeakHashMap();
0134:
0135:            /** Create a ComponentRecorder for use in capturing the semantics of a GUI
0136:             * action.
0137:             */
0138:            public ComponentRecorder(Resolver resolver) {
0139:                super (resolver);
0140:            }
0141:
0142:            /** Does the given event indicate a window was shown? */
0143:            protected boolean isOpen(AWTEvent event) {
0144:                int id = event.getID();
0145:                // 1.3 VMs may generate a WINDOW_OPEN without a COMPONENT_SHOWN
0146:                // (see EventRecorderTest.testClickWithDialog)
0147:                // NOTE: COMPONENT_SHOWN precedes WINDOW_OPENED, but we don't really
0148:                // care in this case, since we're just recording the event, not
0149:                // watching for the component's validity.
0150:                if (((id == WindowEvent.WINDOW_OPENED && !openEventWindows
0151:                        .containsKey(event.getSource())) || id == ComponentEvent.COMPONENT_SHOWN)) {
0152:                    return true;
0153:                }
0154:                return false;
0155:            }
0156:
0157:            /** Does the given event indicate a window was closed? */
0158:            protected boolean isClose(AWTEvent event) {
0159:                int id = event.getID();
0160:                // Window.dispose doesn't generate a HIDDEN event, but it does
0161:                // generate a WINDOW_CLOSED event (1.3/1.4)
0162:                if (((id == WindowEvent.WINDOW_CLOSED && !closeEventWindows
0163:                        .containsKey(event.getSource())) || id == ComponentEvent.COMPONENT_HIDDEN)) {
0164:                    return true;
0165:                }
0166:                return false;
0167:            }
0168:
0169:            /** Returns whether this ComponentRecorder wishes to accept the given
0170:             * event.  If the event is accepted, the recorder must invoke init() with
0171:             * the appropriate semantic event type.
0172:             */
0173:            public boolean accept(AWTEvent event) {
0174:                int rtype = SE_NONE;
0175:
0176:                if (isWindowEvent(event)) {
0177:                    rtype = SE_WINDOW;
0178:                } else if (isMenuEvent(event)) {
0179:                    rtype = SE_MENU;
0180:                } else if (isKeyTyped(event)) {
0181:                    rtype = SE_KEY;
0182:                } else if (isClick(event)) {
0183:                    rtype = SE_CLICK;
0184:                } else if (isDragDrop(event)) {
0185:                    rtype = SE_DROP;
0186:                } else if (isInputMethod(event)) {
0187:                    rtype = SE_IM;
0188:                } else {
0189:                    if (Log.isClassDebugEnabled(ComponentRecorder.class))
0190:                        Log.debug("Ignoring " + Robot.toString(event));
0191:                }
0192:
0193:                init(rtype);
0194:                boolean accepted = rtype != SE_NONE;
0195:                if (accepted
0196:                        && Log.isClassDebugEnabled(ComponentRecorder.class))
0197:                    Log.debug("Accepted " + ComponentTester.toString(event));
0198:                return accepted;
0199:            }
0200:
0201:            /** Test whether the given event is a trigger for a window event. 
0202:             * Allow derived classes to change definition of a click.
0203:             */
0204:            protected boolean isWindowEvent(AWTEvent event) {
0205:                // Ignore activate and deactivate.  They are unreliable.
0206:                // We only want open/close events on non-tooltip and non-popup windows
0207:                return (event.getSource() instanceof  Window)
0208:                        && !AWT.isHeavyweightPopup((Window) event.getSource())
0209:                        && !isToolTip(event.getSource())
0210:                        && (isClose(event) || isOpen(event));
0211:            }
0212:
0213:            /**
0214:             * Return true if the given event source is a tooltip.
0215:             * Such events look like window events, but we check for them before other
0216:             * kinds of window events so as to be able to filter them out.
0217:             * <P>
0218:             * TODO: emit steps to confirm value of tooltip?
0219:             * <P>
0220:             * @param source the object to examine
0221:             * @return true if this event source is a tooltip
0222:             */
0223:            protected boolean isToolTip(Object source) {
0224:                // Tooltips appear to be a direct subclass of JWindow and
0225:                // have a single component of class JToolTip
0226:                if (source instanceof  JWindow && !(source instanceof  JFrame)) {
0227:                    Container pane = ((JWindow) source).getContentPane();
0228:                    while (pane.getComponentCount() == 1) {
0229:                        Component child = pane.getComponent(0);
0230:                        if (child instanceof  JToolTip)
0231:                            return true;
0232:                        if (!(child instanceof  Container))
0233:                            break;
0234:                        pane = (Container) child;
0235:                    }
0236:                }
0237:                return false;
0238:            }
0239:
0240:            protected boolean isMenuEvent(AWTEvent event) {
0241:                if (event.getID() == ActionEvent.ACTION_PERFORMED
0242:                        && event.getSource() instanceof  java.awt.MenuItem) {
0243:                    return true;
0244:                } else if (event.getID() == MouseEvent.MOUSE_PRESSED) {
0245:                    MouseEvent me = (MouseEvent) event;
0246:                    return me.isPopupTrigger()
0247:                            || ((me.getModifiers() & AWTConstants.POPUP_MASK) != 0)
0248:                            || me.getSource() instanceof  JMenu;
0249:                }
0250:                return false;
0251:            }
0252:
0253:            protected boolean isKeyTyped(AWTEvent event) {
0254:                return event.getID() == KeyEvent.KEY_TYPED;
0255:            }
0256:
0257:            /** Test whether the given event is a trigger for a mouse button click.
0258:             * Allow derived classes to change definition of a click.
0259:             */
0260:            protected boolean isClick(AWTEvent event) {
0261:                if (event.getID() == MouseEvent.MOUSE_PRESSED) {
0262:                    MouseEvent me = (MouseEvent) event;
0263:                    return (me.getModifiers() & MouseEvent.BUTTON1_MASK) != 0;
0264:                }
0265:                return false;
0266:            }
0267:
0268:            /** Test whether the given event precurses a drop. */
0269:            protected boolean isDragDrop(AWTEvent event) {
0270:                return event.getID() == MouseEvent.MOUSE_DRAGGED;
0271:            }
0272:
0273:            /** Default to recording a drag if it looks like one. */
0274:            // FIXME may be some better detection, like checking for DND interfaces. */
0275:            protected boolean canDrag() {
0276:                return true;
0277:            }
0278:
0279:            /** Default to waiting for multiple clicks. */
0280:            protected boolean canMultipleClick() {
0281:                return true;
0282:            }
0283:
0284:            /** Is this the start of an input method event? */
0285:            private boolean isInputMethod(AWTEvent event) {
0286:                // NOTE: HALF_WIDTH signals start of kanji input
0287:                // NOTE: Mac uses input method for some dual-keystroke chars (option-e)
0288:                return (event.getID() == KeyEvent.KEY_RELEASED && ((KeyEvent) event)
0289:                        .getKeyCode() == KeyEvent.VK_HALF_WIDTH)
0290:                        || event.getID() == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED;
0291:            }
0292:
0293:            /** Provide standard parsing of mouse button events. */
0294:            protected boolean parseClick(AWTEvent event) {
0295:                boolean consumed = true;
0296:                int id = event.getID();
0297:                if (id == MouseEvent.MOUSE_PRESSED) {
0298:                    Log.debug("Parsing mouse down");
0299:                    MouseEvent me = (MouseEvent) event;
0300:                    if (clickCount == 0) {
0301:                        target = me.getComponent();
0302:                        x = me.getX();
0303:                        y = me.getY();
0304:                        modifiers = me.getModifiers();
0305:                        clickCount = 1;
0306:                        // Add the component immediately, just in case it gets removed
0307:                        // from the hierarchy as a result of the click.
0308:                        getResolver().addComponent(target);
0309:                    } else {
0310:                        if (target == me.getComponent()) {
0311:                            clickCount = me.getClickCount();
0312:                        } else if (!released) {
0313:                            // It's possible to get two consecutive MOUSE_PRESSED
0314:                            // events for different targets (e.g. double click on a
0315:                            // table cell to get the default editor) (OSX 1.3.1, XP
0316:                            // 1.4.1_01). Ignore the second click, since it is
0317:                            // artificial, and wait for the original click to finish.
0318:                            // i.e. w32 1.3.1
0319:                            // MOUSE_PRESSED  JTable
0320:                            // MOUSE_PRESSED  JTextField
0321:                            // FOCUS_LOST     JTable
0322:                            // FOCUS_GAINED   JTextField
0323:                            // MOUSE_EXITED   JTable
0324:                            // MOUSE_ENTERED  JTextField
0325:                            // MOUSE_RELEASED JTable
0326:                            // MOUSE_RELEASED JTextField
0327:                            forwardedTarget = me.getComponent();
0328:                        }
0329:                    }
0330:                    released = false;
0331:                } else if (id == MouseEvent.MOUSE_RELEASED) {
0332:                    Log.debug("Parsing mouse up");
0333:                    released = true;
0334:                    // Optionally disallow multiple clicks
0335:                    if (!canMultipleClick())
0336:                        setFinished(true);
0337:                } else if (id == MouseEvent.MOUSE_CLICKED) {
0338:                    // optionally wait for multiple clicks
0339:                    if (!canMultipleClick())
0340:                        setFinished(true);
0341:                } else if (id == MouseEvent.MOUSE_EXITED) {
0342:                    Log.debug("exit event, released=" + released);
0343:                    if (event.getSource() != target || released) {
0344:                        consumed = false;
0345:                        setFinished(true);
0346:                    } else if (!released) {
0347:                        // May not see any DRAGGED events if it's a native drag;
0348:                        // 1.3 posts MOUSE_EXITED after MOUSE_PRESSED, no drag events
0349:                        if (clickCount == 1) {
0350:                            setRecordingType(SE_DRAG);
0351:                            consumed = dragStarted(target, x, y, modifiers,
0352:                                    (MouseEvent) event);
0353:                        }
0354:                    }
0355:                } else if (id == MouseEvent.MOUSE_ENTERED) {
0356:                    if (event.getSource() == target && !released) {
0357:                        // nothing
0358:                    } else if (event.getSource() != forwardedTarget) {
0359:                        consumed = false;
0360:                        setFinished(true);
0361:                    }
0362:                } else if (id == MouseEvent.MOUSE_DRAGGED && canDrag()) {
0363:                    Log.debug("Changing click to drag start");
0364:                    MouseEvent me = (MouseEvent) event;
0365:                    Point where = SwingUtilities.convertPoint(
0366:                            me.getComponent(), me.getX(), me.getY(), target);
0367:                    int threshold = Integer.getInteger(
0368:                            "abbot.capture.drag_threshold", 5).intValue();
0369:
0370:                    if (Math.abs(where.x - x) >= threshold
0371:                            || Math.abs(where.y - y) >= threshold) {
0372:                        // Was actually a drag; pass off to drag handler
0373:                        setRecordingType(SE_DRAG);
0374:                        consumed = dragStarted(target, x, y, modifiers, me);
0375:                    } else {
0376:                        Log.debug("Drag too small: " + me + " (from "
0377:                                + dragSource + " (" + x + ", " + y + "))");
0378:                    }
0379:                }
0380:                // These events will not prevent a multi-click from being registered.
0381:                else if ((id >= ComponentEvent.COMPONENT_FIRST && id <= ComponentEvent.COMPONENT_LAST)
0382:                        || (event instanceof  ContainerEvent)
0383:                        || (event instanceof  FocusEvent)
0384:                        || (id == HierarchyEvent.HIERARCHY_CHANGED && (((HierarchyEvent) event)
0385:                                .getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) == 0)) {
0386:                    // Ignore most hierarchy change and component events between
0387:                    // clicks. 
0388:                    // The focus event is sporadic on w32 1.4.1_02
0389:                } else {
0390:                    // All other events should cause the click to finish,
0391:                    // but don't register a click unless we've received the release
0392:                    // event. 
0393:                    if (released) {
0394:                        consumed = false;
0395:                        setFinished(true);
0396:                    }
0397:                }
0398:                return consumed;
0399:            }
0400:
0401:            protected boolean parseWindowEvent(AWTEvent event) {
0402:                boolean consumed = true;
0403:                isClose = isClose(event);
0404:                // Keep track of window open/close state so we don't parse the same
0405:                // semantic event twice (e.g. COMPONENT_SHOWN + WINDOW_OPENED or
0406:                // multiple WINDOW_CLOSED events).
0407:                if (isClose) {
0408:                    closeEventWindows.put(event.getSource(), Boolean.TRUE);
0409:                    openEventWindows.remove(event.getSource());
0410:                } else {
0411:                    openEventWindows.put(event.getSource(), Boolean.TRUE);
0412:                    closeEventWindows.remove(event.getSource());
0413:                }
0414:                Log
0415:                        .log("close=" + isClose + " (" + Robot.toString(event)
0416:                                + ")");
0417:                window = (Window) event.getSource();
0418:                setFinished(true);
0419:                return consumed;
0420:            }
0421:
0422:            protected boolean parseKeyEvent(AWTEvent e) {
0423:                int id = e.getID();
0424:                boolean consumed = true;
0425:                if (id == KeyEvent.KEY_TYPED) {
0426:                    KeyEvent typed = (KeyEvent) e;
0427:                    target = typed.getComponent();
0428:                    keychar = typed.getKeyChar();
0429:                    modifiers = typed.getModifiers();
0430:                    if ((modifiers & KeyEvent.ALT_MASK) == KeyEvent.ALT_MASK) {
0431:                        Log
0432:                                .debug("Waiting for potential focus accelerator on '"
0433:                                        + keychar + "'");
0434:                    } else {
0435:                        // Ignore KEY_TYPED input for control and alt modifiers, since
0436:                        // the generated characters are not accepted as text input.
0437:                        // Add others if you encounter them, but err on the side of
0438:                        // accepting input that can later be removed.
0439:                        if ((modifiers & InputEvent.CTRL_MASK) == InputEvent.CTRL_MASK
0440:                                || (modifiers & InputEvent.ALT_MASK) == InputEvent.ALT_MASK) {
0441:                            Log.debug("Ignoring modifiers: " + modifiers);
0442:                            setRecordingType(SE_NONE);
0443:                        }
0444:                        setFinished(true);
0445:                    }
0446:                } else if (id == FocusEvent.FOCUS_LOST) {
0447:                    // Ignore and wait for FOCUS_GAINED
0448:                } else if (id == FocusEvent.FOCUS_GAINED) {
0449:                    // Looks like a focus accelerator focus change.  Ignore the
0450:                    // KEY_TYPED event.
0451:                    Object o = e.getSource();
0452:                    char ch = KeyEvent.CHAR_UNDEFINED;
0453:                    if (o instanceof  JTextComponent) {
0454:                        ch = ((JTextComponent) o).getFocusAccelerator();
0455:                        Log.debug("focus accelerator is '" + ch + "'");
0456:                    }
0457:                    if (Character.toUpperCase(ch) == Character
0458:                            .toUpperCase(keychar)) {
0459:                        setRecordingType(SE_NONE);
0460:                        setFinished(true);
0461:                    } else {
0462:                        setFinished(true);
0463:                        consumed = false;
0464:                    }
0465:                } else {
0466:                    setRecordingType(SE_NONE);
0467:                    setFinished(true);
0468:                    consumed = false;
0469:                }
0470:                return consumed;
0471:            }
0472:
0473:            /** Base implementation handles context (popup) menus. */
0474:            protected boolean parseMenuSelection(AWTEvent event) {
0475:                int id = event.getID();
0476:                boolean consumed = true;
0477:                // press, release, show, [move, show,] press, release
0478:                // press, [drag, show,] release (FIXME not done)
0479:                // ACTION_PERFORMED and ITEM_STATE_CHANGED are only 
0480:                // produced by AWT components (wxp/1.4.2)
0481:                if (id == ActionEvent.ACTION_PERFORMED
0482:                        || id == ItemEvent.ITEM_STATE_CHANGED) {
0483:                    awtMenuTarget = (MenuItem) event.getSource();
0484:                    invoker = AWT.getInvoker(awtMenuTarget);
0485:                    // If there is no invoker, the selection came from the MenuBar
0486:                    if (invoker != null) {
0487:                        isPopup = true;
0488:                    }
0489:                    Log.debug("AWT menu selection, invoker="
0490:                            + Robot.toString(invoker));
0491:                    if (event instanceof  ActionEvent) {
0492:                        modifiers = ((ActionEvent) event).getModifiers();
0493:                    } else {
0494:                        // ItemEvent doesn't report modifiers, so ask use internal
0495:                        // tracking to see if any modifiers are active.
0496:                        modifiers = Robot.getState().getModifiers();
0497:                    }
0498:                    setFinished(true);
0499:                } else if (id == MouseEvent.MOUSE_PRESSED) {
0500:                    MouseEvent me = (MouseEvent) event;
0501:                    // On the first press, we haven't yet set the invoker, which
0502:                    // is either a JMenu or the component holding the popup.
0503:                    if (invoker == null) {
0504:                        invoker = me.getComponent();
0505:                        menux = me.getX();
0506:                        menuy = me.getY();
0507:                        modifiers = me.getModifiers();
0508:                        isPopup = me.isPopupTrigger();
0509:                        // Must add the listener now, b/c on w32 release/click events
0510:                        // are not generated until *after* the awt popup selection.
0511:                        if (isPopup
0512:                                || (modifiers & AWTConstants.POPUP_MASK) != 0) {
0513:                            hasAWTPopup = addMenuListener(invoker);
0514:                        }
0515:                        // It's possible for a popup menu to be triggered by some
0516:                        // other event (e.g. a button click).  Assume that action is
0517:                        // already recorded and simply make note of the appropriate
0518:                        // menu selection.
0519:                        if (invoker instanceof  JMenuItem
0520:                                && !(invoker instanceof  JMenu)) {
0521:                            menuTarget = invoker;
0522:                            invoker = null;
0523:                            menux = menuy = -1;
0524:                            modifiers = 0;
0525:                            isPopup = true;
0526:                        }
0527:                    } else if (event.getSource() instanceof  JMenu) {
0528:                        // ignore
0529:                    } else if (event.getSource() instanceof  JMenuItem) {
0530:                        // Click to select the menu item; this will be the second
0531:                        // press event received
0532:                        menuTarget = (Component) event.getSource();
0533:                    } else {
0534:                        // Mouse press in something other than the menu, assume it was
0535:                        // canceled. 
0536:                        // Popup was canceled.  Discard subsequent release/click.
0537:                        menuCanceled = true;
0538:                        setStatus("Popup menu selection canceled");
0539:                    }
0540:                    Log.log("Menu mouse press");
0541:                } else if (id == MouseEvent.MOUSE_RELEASED) {
0542:                    MouseEvent me = (MouseEvent) event;
0543:                    // The menu target won't be set until the second mouse press
0544:                    if (menuCanceled) {
0545:                        setRecordingType(SE_NONE);
0546:                        setFinished(true);
0547:                    } else if (menuTarget == null) {
0548:                        // This is the first mouse release
0549:                        if (!isPopup) {
0550:                            isPopup = me.isPopupTrigger();
0551:                        }
0552:                    } else {
0553:                        if (menuTarget != null)
0554:                            setFinished(true);
0555:                    }
0556:                    Log.log("Menu mouse release");
0557:                } else if (id == MouseEvent.MOUSE_CLICKED && isPopup) {
0558:                    // If it was a popup trigger, make sure there was a popup,
0559:                    // otherwise record it as a click.
0560:                    // Note that we won't likely get any events with an AWT popup, so
0561:                    // just assume it was invoked if there is one.
0562:                    if (!hasAWTPopup && AWT.findActivePopupMenu() == null) {
0563:                        setRecordingType(SE_CLICK);
0564:                        target = invoker;
0565:                        x = menux;
0566:                        y = menuy;
0567:                        setFinished(true);
0568:                    }
0569:                } else {
0570:                    Log.debug("Ignoring " + ComponentTester.toString(event));
0571:                }
0572:                return consumed;
0573:            }
0574:
0575:            protected boolean parseDrop(AWTEvent event) {
0576:                int id = event.getID();
0577:
0578:                switch (id) {
0579:                case MouseEvent.MOUSE_DRAGGED:
0580:                case MouseEvent.MOUSE_ENTERED:
0581:                    updateDropTarget((MouseEvent) event);
0582:                    break;
0583:                case MouseEvent.MOUSE_RELEASED:
0584:                    updateDropTarget((MouseEvent) event);
0585:                    setFinished(true);
0586:                    break;
0587:                case MouseEvent.MOUSE_MOVED:
0588:                    setFinished(true);
0589:                    break;
0590:                case MouseEvent.MOUSE_EXITED:
0591:                    // ignore this; wait for the next event to get
0592:                    // a new drop target
0593:                    break;
0594:                default:
0595:                    if (Log.isClassDebugEnabled(ComponentRecorder.class))
0596:                        Log
0597:                                .debug("Ignoring "
0598:                                        + ComponentTester.toString(event));
0599:                }
0600:                return true;
0601:            }
0602:
0603:            /** Non-native drags use the drag source as the source for all mouse events
0604:             * during the drag except for enter/exit events.  Native drags use the
0605:             * parent window as the source for all SunDropTargetEvents.  In both cases,
0606:             * extract the actual target and location relative to the target.
0607:             */
0608:            private void updateDropTarget(MouseEvent me) {
0609:                dropTarget = SwingUtilities.getDeepestComponentAt(me
0610:                        .getComponent(), me.getX(), me.getY());
0611:                Point where = SwingUtilities.convertPoint(me.getComponent(), me
0612:                        .getX(), me.getY(), dropTarget);
0613:                dropx = where.x;
0614:                dropy = where.y;
0615:            }
0616:
0617:            protected boolean parseInputMethod(AWTEvent event) {
0618:                boolean consumed = true;
0619:                int id = event.getID();
0620:                if (id == KeyEvent.KEY_RELEASED) {
0621:                    KeyEvent ke = (KeyEvent) event;
0622:                    int code = ke.getKeyCode();
0623:                    switch (code) {
0624:                    case KeyEvent.VK_HALF_WIDTH:
0625:                        // This indicates the input method start (for kanji, anyway)
0626:                        break;
0627:                    case KeyEvent.VK_FULL_WIDTH:
0628:                        // This indicates the input method end (for kanji, anyway)
0629:                        Log.log("Captured " + imText);
0630:                        setFinished(true);
0631:                        break;
0632:                    case KeyEvent.VK_ALT_GRAPH:
0633:                    case KeyEvent.VK_CONTROL:
0634:                    case KeyEvent.VK_SHIFT:
0635:                    case KeyEvent.VK_META:
0636:                    case KeyEvent.VK_ALT:
0637:                        Log.debug("Modifier indicates end of InputMethod");
0638:                        consumed = true;
0639:                        setFinished(true);
0640:                        break;
0641:                    default:
0642:                        // Consume other key release events, assuming there was no
0643:                        // corresponding key press event.
0644:                        imKeyCodes.add(new Integer(code));
0645:                        break;
0646:                    }
0647:                } else if (event instanceof  InputMethodEvent) {
0648:                    InputMethodEvent ime = (InputMethodEvent) event;
0649:                    if (id == InputMethodEvent.INPUT_METHOD_TEXT_CHANGED) {
0650:                        if (ime.getCommittedCharacterCount() > 0) {
0651:                            AttributedCharacterIterator iter = ime.getText();
0652:                            StringBuffer sb = new StringBuffer();
0653:                            for (char ch = iter.first(); ch != CharacterIterator.DONE; ch = iter
0654:                                    .next()) {
0655:                                sb.append(ch);
0656:                            }
0657:                            target = (Component) ime.getSource();
0658:                            imText.append(sb.toString());
0659:                            Log.debug("Partial capture " + sb.toString());
0660:                        }
0661:                        setFinished(true);
0662:                    }
0663:                } else {
0664:                    consumed = false;
0665:                    setFinished(true);
0666:                }
0667:                return consumed;
0668:            }
0669:
0670:            /** Handle an event.  Return whether the event was consumed. */
0671:            public boolean parse(AWTEvent event) {
0672:                if (Log.isClassDebugEnabled(ComponentRecorder.class))
0673:                    Log.debug("Parsing " + ComponentTester.toString(event)
0674:                            + " as " + TYPES[getRecordingType()]);
0675:
0676:                // Default handling is event consumed, and assume not finished
0677:                boolean consumed = true;
0678:
0679:                switch (getRecordingType()) {
0680:                case SE_WINDOW:
0681:                    consumed = parseWindowEvent(event);
0682:                    break;
0683:                case SE_KEY:
0684:                    consumed = parseKeyEvent(event);
0685:                    break;
0686:                case SE_CLICK:
0687:                    consumed = parseClick(event);
0688:                    break;
0689:                case SE_MENU:
0690:                    consumed = parseMenuSelection(event);
0691:                    break;
0692:                case SE_DROP:
0693:                    consumed = parseDrop(event);
0694:                    break;
0695:                case SE_IM:
0696:                    consumed = parseInputMethod(event);
0697:                    break;
0698:                default:
0699:                    Log.warn("Unknown input type: " + getRecordingType());
0700:                    // error
0701:                    break;
0702:                }
0703:                if (isFinished()) {
0704:                    try {
0705:                        Step step = createStep();
0706:                        setStep(step);
0707:                        Log.log("Semantic event recorded: " + step);
0708:                    } catch (Throwable thr) {
0709:                        String msg = Strings.get("editor.recording.error");
0710:                        BugReport br = new BugReport(msg, thr);
0711:                        Log.log("Semantic recorder error: " + br.toString());
0712:                        setStatus(Strings.get("editor.see_console"));
0713:                        setRecordingType(SE_NONE);
0714:                        throw br;
0715:                    }
0716:                }
0717:
0718:                return consumed;
0719:            }
0720:
0721:            /** Returns whether the first drag motion event should be consumed.
0722:             * Derived classes may override this to provide custom drag behavior.
0723:             * Default behavior saves the drag initiation event by itself.
0724:             */
0725:            protected boolean dragStarted(Component target, int x, int y,
0726:                    int modifiers, MouseEvent dragEvent) {
0727:                dragSource = target;
0728:                dragx = x;
0729:                dragy = y;
0730:                setFinished(true);
0731:                return false;
0732:            }
0733:
0734:            /** Returns the script step generated from the events recorded so far. */
0735:            protected Step createStep() {
0736:                Step step = null;
0737:                int type = getRecordingType();
0738:                Log
0739:                        .debug("Creating step for semantic recorder, type: "
0740:                                + (type >= 0 && type < TYPES.length ? TYPES[getRecordingType()]
0741:                                        : String.valueOf(type)));
0742:                switch (type) {
0743:                case SE_WINDOW:
0744:                    step = createWindowEvent(window, isClose);
0745:                    break;
0746:                case SE_MENU:
0747:                    if (awtMenuTarget != null) {
0748:                        if (invoker == null) {
0749:                            MenuContainer mc = awtMenuTarget.getParent();
0750:                            while (mc instanceof  MenuComponent
0751:                                    && !(mc instanceof  Component)) {
0752:                                mc = ((MenuComponent) mc).getParent();
0753:                            }
0754:                            if (mc == null) {
0755:                                throw new Error("AWT MenuItem " + awtMenuTarget
0756:                                        + " has no Component ancestor");
0757:                            }
0758:                            invoker = (Component) mc;
0759:                        }
0760:                        step = createAWTMenuSelection(invoker, awtMenuTarget,
0761:                                isPopup);
0762:                    } else if (isPopup) {
0763:                        step = createPopupMenuSelection(invoker, menux, menuy,
0764:                                menuTarget);
0765:                    } else if (menuTarget != null) {
0766:                        step = createMenuSelection(menuTarget);
0767:                    }
0768:                    break;
0769:                case SE_KEY: {
0770:                    if (keychar != KeyEvent.CHAR_UNDEFINED) {
0771:                        step = createKey(target, keychar, modifiers);
0772:                    } else {
0773:                        step = null;
0774:                    }
0775:                    break;
0776:                }
0777:                case SE_CLICK: {
0778:                    step = createClick(target, x, y, modifiers,
0779:                            canMultipleClick() ? clickCount : 1);
0780:                    break;
0781:                }
0782:                case SE_DRAG: {
0783:                    step = createDrag(dragSource, dragx, dragy);
0784:                    break;
0785:                }
0786:                case SE_DROP:
0787:                    step = createDrop(dropTarget, dropx, dropy);
0788:                    break;
0789:                case SE_IM:
0790:                    if (imText.length() > 0)
0791:                        step = createInputMethod(target, imKeyCodes, imText
0792:                                .toString());
0793:                    else {
0794:                        Log.debug("Input method resulted in no text");
0795:                        step = null;
0796:                    }
0797:                    break;
0798:                default:
0799:                    step = null;
0800:                    break;
0801:                }
0802:
0803:                return step;
0804:            }
0805:
0806:            /** Create a wait for the window show/hide.  Use an appropriate identifier
0807:                string, which might be the name, title, or component reference.
0808:             */
0809:            protected Step createWindowEvent(Window window, boolean isClose) {
0810:                ComponentReference ref = getResolver().addComponent(window);
0811:                String method = "assertComponentShowing";
0812:                Assert step = new Assert(getResolver(), null,
0813:                        ComponentTester.class.getName(), method,
0814:                        new String[] { ref.getID() }, "true", isClose);
0815:                step.setWait(true);
0816:                return step;
0817:            }
0818:
0819:            protected Step createMenuSelection(Component menuItem) {
0820:                ComponentReference cr = getResolver().addComponent(menuItem);
0821:                Step step = new Action(getResolver(), null,
0822:                        "actionSelectMenuItem", new String[] { cr.getID() });
0823:                return step;
0824:            }
0825:
0826:            protected Step createAWTMenuSelection(Component parent,
0827:                    MenuItem menuItem, boolean isPopup) {
0828:                ComponentReference ref = getResolver().addComponent(parent);
0829:                String method = "actionSelectAWTMenuItem";
0830:                if (isPopup)
0831:                    method = "actionSelectAWTPopupMenuItem";
0832:                // Get a unique path for the MenuItem
0833:                String path = AWT.getPath(menuItem);
0834:                // Do a quick search on the invoker for other popups.  If there are
0835:                // duplicates, include the menu item name
0836:                Step step = new Action(getResolver(), null, method,
0837:                        new String[] { ref.getID(), path });
0838:                return step;
0839:            }
0840:
0841:            protected Step createPopupMenuSelection(Component invoker, int x,
0842:                    int y, Component menuItem) {
0843:                Step step;
0844:                if (invoker != null) {
0845:                    ComponentReference inv = getResolver()
0846:                            .addComponent(invoker);
0847:                    JMenuItem mi = (JMenuItem) menuItem;
0848:                    String where = getLocationArgument(invoker, x, y);
0849:                    step = new Action(getResolver(), null,
0850:                            "actionSelectPopupMenuItem", new String[] {
0851:                                    inv.getID(), where,
0852:                                    JMenuItemMatcher.getPath(mi) }, invoker
0853:                                    .getClass());
0854:                } else {
0855:                    ComponentReference ref = getResolver().addComponent(
0856:                            menuItem);
0857:                    step = new Action(getResolver(), null,
0858:                            "actionSelectMenuItem",
0859:                            new String[] { ref.getID() });
0860:                }
0861:                return step;
0862:            }
0863:
0864:            protected Step createKey(Component comp, char keychar, int mods) {
0865:                ComponentReference cr = getResolver().addComponent(comp);
0866:                // NOTE: Any keys which might have effects as key press/release should
0867:                // be encoded as a keystroke, rather than a keystring.
0868:                // NOTE: We encode strings rather than integer values, since the
0869:                // names are more useful.
0870:                String code = (String) specialMap.get(String.valueOf(keychar));
0871:                if (code != null) {
0872:                    String[] args = mods != 0 ? new String[] { cr.getID(),
0873:                            code, AWT.getKeyModifiers(mods) } : new String[] {
0874:                            cr.getID(), code };
0875:                    return new Action(getResolver(), null, "actionKeyStroke",
0876:                            args);
0877:                }
0878:                return new Action(getResolver(), null, "actionKeyString",
0879:                        new String[] { cr.getID(), String.valueOf(keychar) });
0880:            }
0881:
0882:            protected Step createDrop(Component comp, int x, int y) {
0883:                Step step = null;
0884:                if (comp != null) {
0885:                    ComponentReference cr = getResolver().addComponent(comp);
0886:                    String where = getLocationArgument(comp, x, y);
0887:                    step = new Action(getResolver(), null, "actionDrop",
0888:                            new String[] { cr.getID(), where }, comp.getClass());
0889:                }
0890:                return step;
0891:            }
0892:
0893:            protected Step createDrag(Component comp, int x, int y) {
0894:                ComponentReference ref = getResolver().addComponent(comp);
0895:                String where = getLocationArgument(comp, x, y);
0896:                Step step = new Action(getResolver(), null, "actionDrag",
0897:                        new String[] { ref.getID(), where, }, comp.getClass());
0898:                return step;
0899:            }
0900:
0901:            /** Create a click event with the given event information. */
0902:            protected Step createClick(Component target, int x, int y,
0903:                    int mods, int count) {
0904:                Log.debug("creating click");
0905:                ComponentReference cr = getResolver().addComponent(target);
0906:                ArrayList args = new ArrayList();
0907:                args.add(cr.getID());
0908:                args.add(getLocationArgument(target, x, y));
0909:                if ((mods != 0 && mods != MouseEvent.BUTTON1_MASK) || count > 1) {
0910:                    // NOTE: this currently saves POPUP or TERTIARY, rather than
0911:                    // an explicit button 2 or 3.  I figure that makes more sense
0912:                    // than a hard coded button number.
0913:                    args.add(AWT.getMouseModifiers(mods));
0914:                    if (count > 1) {
0915:                        args.add(String.valueOf(count));
0916:                    }
0917:                }
0918:                return new Action(getResolver(), null, "actionClick",
0919:                        (String[]) args.toArray(new String[args.size()]),
0920:                        target.getClass());
0921:            }
0922:
0923:            protected Step createInputMethod(Component comp, ArrayList codes,
0924:                    String text) {
0925:                Log.debug("Text length is " + text.length());
0926:                ComponentReference ref = getResolver().addComponent(comp);
0927:                return new Action(getResolver(), null, "actionKeyString",
0928:                        new String[] { ref.getID(), text });
0929:            }
0930:
0931:            protected void init(int recordingType) {
0932:                super .init(recordingType);
0933:                target = null;
0934:                forwardedTarget = null;
0935:                released = false;
0936:                clickCount = 0;
0937:                keychar = KeyEvent.CHAR_UNDEFINED;
0938:                invoker = null;
0939:                awtMenuTarget = null;
0940:                isPopup = false;
0941:                hasAWTPopup = false;
0942:                menuListener = null;
0943:                menuTarget = null;
0944:                menuCanceled = false;
0945:                dragSource = dropTarget = null;
0946:                window = null;
0947:                isClose = false;
0948:                imKeyCodes.clear();
0949:                imText.delete(0, imText.length());
0950:            }
0951:
0952:            /** Invoke when end of the semantic event has been seen. */
0953:            protected void setFinished(boolean state) {
0954:                MenuListener listener = null;
0955:                synchronized (this ) {
0956:                    super .setFinished(state);
0957:                    listener = menuListener;
0958:                    menuListener = null;
0959:                }
0960:                if (listener != null)
0961:                    listener.dispose();
0962:            }
0963:
0964:            private boolean addMenuListener(Component invoker) {
0965:                PopupMenu[] popups = AWT.getPopupMenus(invoker);
0966:                if (popups.length > 0) {
0967:                    menuListener = new MenuListener(popups);
0968:                    return true;
0969:                }
0970:                return false;
0971:            }
0972:
0973:            private class MenuListener implements  ItemListener {
0974:                private ArrayList items = new ArrayList();
0975:
0976:                public MenuListener(PopupMenu[] popups) {
0977:                    for (int i = 0; i < popups.length; i++) {
0978:                        addRecursive(popups[i]);
0979:                    }
0980:                }
0981:
0982:                private void addRecursive(Menu menu) {
0983:                    for (int i = 0; i < menu.getItemCount(); i++) {
0984:                        MenuItem item = menu.getItem(i);
0985:                        if (item instanceof  Menu)
0986:                            addRecursive((Menu) item);
0987:                        else if (item instanceof  CheckboxMenuItem) {
0988:                            ((CheckboxMenuItem) item).addItemListener(this );
0989:                            items.add(item);
0990:                        }
0991:                    }
0992:                }
0993:
0994:                public void itemStateChanged(ItemEvent e) {
0995:                    dispose();
0996:                    parse(e);
0997:                }
0998:
0999:                public void dispose() {
1000:                    while (items.size() > 0) {
1001:                        ((CheckboxMenuItem) items.get(0))
1002:                                .removeItemListener(this );
1003:                        items.remove(0);
1004:                    }
1005:                }
1006:            }
1007:
1008:            /** Obtain the String representation of the Component-specific location. */
1009:            protected String getLocationArgument(Component c, int x, int y) {
1010:                return getLocation(c, x, y).toString();
1011:            }
1012:
1013:            /** Obtain a more precise location than the given coordinate, if
1014:             * possible.
1015:             */
1016:            protected ComponentLocation getLocation(Component c, int x, int y) {
1017:                ComponentTester tester = ComponentTester.getTester(c);
1018:                return tester.getLocation(c, new Point(x, y));
1019:            }
1020:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.