001: /*******************************************************************************
002: * Copyright (c) 2000, 2005 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.pde.internal.ui.tests.macro;
011:
012: import java.io.PrintWriter;
013: import java.util.ArrayList;
014: import java.util.Hashtable;
015:
016: import org.eclipse.core.runtime.CoreException;
017: import org.eclipse.core.runtime.IPath;
018: import org.eclipse.core.runtime.IProgressMonitor;
019: import org.eclipse.core.runtime.IStatus;
020: import org.eclipse.core.runtime.Path;
021: import org.eclipse.core.runtime.Status;
022: import org.eclipse.core.runtime.SubProgressMonitor;
023: import org.eclipse.jface.window.Window;
024: import org.eclipse.swt.SWT;
025: import org.eclipse.swt.SWTError;
026: import org.eclipse.swt.SWTException;
027: import org.eclipse.swt.custom.CCombo;
028: import org.eclipse.swt.custom.CTabFolder;
029: import org.eclipse.swt.custom.StyledText;
030: import org.eclipse.swt.custom.TableTree;
031: import org.eclipse.swt.widgets.Button;
032: import org.eclipse.swt.widgets.Combo;
033: import org.eclipse.swt.widgets.Composite;
034: import org.eclipse.swt.widgets.Control;
035: import org.eclipse.swt.widgets.Display;
036: import org.eclipse.swt.widgets.Event;
037: import org.eclipse.swt.widgets.Listener;
038: import org.eclipse.swt.widgets.MenuItem;
039: import org.eclipse.swt.widgets.Shell;
040: import org.eclipse.swt.widgets.TabFolder;
041: import org.eclipse.swt.widgets.Table;
042: import org.eclipse.swt.widgets.Text;
043: import org.eclipse.swt.widgets.ToolItem;
044: import org.eclipse.swt.widgets.Tree;
045: import org.eclipse.swt.widgets.Widget;
046: import org.eclipse.ui.PlatformUI;
047: import org.eclipse.ui.commands.IWorkbenchCommandSupport;
048: import org.eclipse.ui.keys.KeySequence;
049: import org.eclipse.ui.keys.KeyStroke;
050: import org.w3c.dom.Node;
051: import org.w3c.dom.NodeList;
052:
053: public class MacroCommandShell extends MacroInstruction {
054: private ArrayList commands;
055:
056: private int expectedReturnCode;
057:
058: private transient Event lastEvent;
059:
060: private transient Display display;
061:
062: private transient Shell shell;
063:
064: private transient IIndexHandler indexHandler;
065:
066: private transient Window window;
067:
068: private static class NestedShell implements Listener, Runnable {
069: private MacroCommandShell cshell;
070:
071: private Display display;
072:
073: private Shell nshell;
074:
075: private boolean released;
076:
077: private CoreException exception;
078:
079: private IProgressMonitor monitor;
080:
081: public NestedShell(Display display, MacroCommandShell cshell,
082: IProgressMonitor monitor) {
083: this .display = display;
084: this .cshell = cshell;
085: this .monitor = monitor;
086: }
087:
088: public void handleEvent(Event e) {
089: if (e.widget instanceof Shell) {
090: // shell activated
091: Shell shell = (Shell) e.widget;
092: IPath path = MacroUtil.getShellId(shell);
093: String sid = path.toString();
094: if (sid.equals(cshell.getId())) {
095: shell.getDisplay().removeFilter(SWT.Activate, this );
096: released = true;
097: this .nshell = shell;
098: shell.getDisplay().asyncExec(this );
099: }
100: }
101: }
102:
103: public boolean getResult() {
104: return cshell.matchesReturnCode();
105: }
106:
107: public boolean isReleased() {
108: return released;
109: }
110:
111: public void run() {
112: try {
113: cshell.playback(display, nshell, monitor);
114: } catch (CoreException e) {
115: this .exception = e;
116: if (nshell != null && !nshell.isDisposed())
117: nshell.close();
118: }
119: }
120:
121: public CoreException getException() {
122: return exception;
123: }
124: }
125:
126: public MacroCommandShell() {
127: this (null, null);
128: }
129:
130: public MacroCommandShell(Shell shell, String path) {
131: super (path);
132: commands = new ArrayList();
133: this .shell = shell;
134: hookWindow(false);
135: }
136:
137: private void hookWindow(boolean playback) {
138: if (shell != null) {
139: if (!playback)
140: doHookWindow();
141: else
142: display.syncExec(new Runnable() {
143: public void run() {
144: doHookWindow();
145: }
146: });
147: }
148: }
149:
150: private void doHookWindow() {
151: Object data = shell.getData();
152: if (data != null && data instanceof Window)
153: this .window = (Window) data;
154: }
155:
156: public void load(Node node, Hashtable lineTable) {
157: super .load(node, lineTable);
158:
159: String codeId = MacroUtil.getAttribute(node, "return-code");
160: if (codeId != null) {
161: try {
162: expectedReturnCode = new Integer(codeId).intValue();
163: } catch (NumberFormatException e) {
164: }
165: }
166: NodeList children = node.getChildNodes();
167: for (int i = 0; i < children.getLength(); i++) {
168: Node child = children.item(i);
169: if (child.getNodeType() == Node.ELEMENT_NODE) {
170: String name = child.getNodeName();
171: if (name.equals("command"))
172: processCommand(child, lineTable);
173: else if (name.equals("shell"))
174: processShell(child, lineTable);
175: else if (name.equals("index"))
176: processIndex(child, lineTable);
177: }
178: }
179: }
180:
181: private void processCommand(Node node, Hashtable lineTable) {
182: String wid = MacroUtil.getAttribute(node, "widgetId");
183: String cid = MacroUtil.getAttribute(node, "contextId");
184: String type = MacroUtil.getAttribute(node, "type");
185: if (type == null)
186: return;
187: MacroCommand command = null;
188: WidgetIdentifier wi = (wid != null && cid != null) ? new WidgetIdentifier(
189: new Path(wid), new Path(cid))
190: : null;
191: if (type.equals(ModifyCommand.TYPE))
192: command = new ModifyCommand(wi);
193: else if (type.equals(BooleanSelectionCommand.TYPE))
194: command = new BooleanSelectionCommand(wi);
195: else if (type.equals(StructuredSelectionCommand.ITEM_SELECT)
196: || type
197: .equals(StructuredSelectionCommand.DEFAULT_SELECT))
198: command = new StructuredSelectionCommand(wi, type);
199: else if (type.equals(ExpansionCommand.TYPE))
200: command = new ExpansionCommand(wi);
201: else if (type.equals(CheckCommand.TYPE))
202: command = new CheckCommand(wi);
203: else if (type.equals(FocusCommand.TYPE))
204: command = new FocusCommand(wi);
205: else if (type.equals(ChoiceSelectionCommand.TYPE))
206: command = new ChoiceSelectionCommand(wi);
207: else if (type.equals(WaitCommand.TYPE))
208: command = new WaitCommand();
209: if (command != null) {
210: command.load(node, lineTable);
211: commands.add(command);
212: }
213: }
214:
215: private void processShell(Node node, Hashtable lineTable) {
216: MacroCommandShell shell = new MacroCommandShell();
217: shell.load(node, lineTable);
218: commands.add(shell);
219: }
220:
221: private void processIndex(Node node, Hashtable lineTable) {
222: MacroIndex index = new MacroIndex();
223: index.load(node, lineTable);
224: commands.add(index);
225: }
226:
227: public void addCommandShell(MacroCommandShell cshell) {
228: commands.add(cshell);
229: }
230:
231: public void write(String indent, PrintWriter writer) {
232: writer.print(indent);
233: writer.print("<shell id=\"");
234: writer.print(getId());
235: writer.print("\" return-code=\"");
236: writer.print(expectedReturnCode + "");
237: writer.println("\">");
238: String cindent = indent + " ";
239: for (int i = 0; i < commands.size(); i++) {
240: IWritable writable = (IWritable) commands.get(i);
241: if (i < commands.size() - 1
242: || !(writable instanceof WaitCommand))
243: writable.write(cindent, writer);
244: }
245: writer.print(indent);
246: writer.println("</shell>");
247: }
248:
249: public void addEvent(Event event) {
250: if (event.widget instanceof Control) {
251: if (((Control) event.widget).isVisible() == false)
252: return;
253: }
254: MacroCommand command = createCommand(event);
255: if (command != null) {
256: command.processEvent(event);
257: MacroCommand lastCommand = getLastCommand();
258: if (lastCommand != null
259: && lastCommand.getWidgetId().equals(
260: command.getWidgetId())
261: && lastCommand.getType().equals(FocusCommand.TYPE)
262: && isFocusCommand(command.getType())) {
263: // focus followed by select or modify - focus implied
264: commands.remove(lastCommand);
265: }
266: commands.add(command);
267: lastEvent = event;
268: }
269: }
270:
271: public void addPause() {
272: WaitCommand command = new WaitCommand();
273: MacroCommand lastCommand = getLastCommand();
274: if (lastCommand != null
275: && lastCommand.getType() != WaitCommand.TYPE)
276: commands.add(command);
277: }
278:
279: public void addIndex(String id) {
280: commands.add(new MacroIndex(id));
281: }
282:
283: public void extractExpectedReturnCode() {
284: if (window != null)
285: expectedReturnCode = window.getReturnCode();
286: }
287:
288: public boolean matchesReturnCode() {
289: if (window != null) {
290: return window.getReturnCode() == expectedReturnCode;
291: }
292: return true;
293: }
294:
295: private boolean isFocusCommand(String type) {
296: return type.equals(BooleanSelectionCommand.TYPE)
297: || type.equals(StructuredSelectionCommand.ITEM_SELECT)
298: || type
299: .equals(StructuredSelectionCommand.DEFAULT_SELECT)
300: || type.equals(ExpansionCommand.TYPE)
301: || type.equals(CheckCommand.TYPE)
302: || type.equals(ModifyCommand.TYPE);
303: }
304:
305: protected MacroCommand createCommand(Event event) {
306: MacroCommand lastCommand = getLastCommand();
307: if (lastEvent != null && lastEvent.widget.equals(event.widget)) {
308: if (lastEvent.type == event.type
309: || (lastEvent.type == SWT.Selection && event.type == SWT.DefaultSelection)) {
310: if (lastCommand != null
311: && lastCommand.mergeEvent(event))
312: return null;
313: }
314: }
315: MacroCommand command = null;
316: WidgetIdentifier wi = MacroUtil
317: .getWidgetIdentifier(event.widget);
318: if (wi == null)
319: return null;
320:
321: switch (event.type) {
322: case SWT.Modify:
323: if (!isEditable(event.widget))
324: return null;
325: command = new ModifyCommand(wi);
326: break;
327: case SWT.Selection:
328: case SWT.DefaultSelection:
329: command = createSelectionCommand(wi, event);
330: break;
331: case SWT.FocusIn:
332: command = new FocusCommand(wi);
333: break;
334: case SWT.Expand:
335: case SWT.Collapse:
336: command = new ExpansionCommand(wi);
337: break;
338: /*
339: * case SWT.KeyUp: command = findKeyBinding(wi, event); break;
340: */
341: }
342: return command;
343: }
344:
345: private boolean isEditable(Widget widget) {
346: if (widget instanceof Control) {
347: Control control = (Control) widget;
348: if (!control.isEnabled())
349: return false;
350: if (control instanceof Text)
351: return ((Text) control).getEditable();
352: if (control instanceof Combo || control instanceof CCombo)
353: return ((control.getStyle() & SWT.READ_ONLY) == 0);
354: if (control instanceof StyledText)
355: return ((StyledText) control).getEditable();
356: }
357: return true;
358: }
359:
360: private MacroCommand createSelectionCommand(WidgetIdentifier wid,
361: Event event) {
362: if (event.widget instanceof MenuItem
363: || event.widget instanceof ToolItem
364: || event.widget instanceof Button) {
365: String wId = wid.getWidgetId();
366: if (wId.endsWith("org.eclipse.pde.ui.tests.StopAction"))
367: return null;
368: if (wId.endsWith("org.eclipse.pde.ui.tests.IndexAction"))
369: return null;
370: return new BooleanSelectionCommand(wid);
371: }
372: if (event.widget instanceof Tree
373: || event.widget instanceof Table
374: || event.widget instanceof TableTree) {
375: if (event.detail == SWT.CHECK)
376: return new CheckCommand(wid);
377: String type = event.type == SWT.DefaultSelection ? StructuredSelectionCommand.DEFAULT_SELECT
378: : StructuredSelectionCommand.ITEM_SELECT;
379: return new StructuredSelectionCommand(wid, type);
380: }
381: if (event.widget instanceof TabFolder
382: || event.widget instanceof CTabFolder)
383: return new ChoiceSelectionCommand(wid);
384: if (event.widget instanceof Combo
385: || event.widget instanceof CCombo)
386: return new ChoiceSelectionCommand(wid);
387: return null;
388: }
389:
390: protected MacroCommand findKeyBinding(WidgetIdentifier wid, Event e) {
391: System.out.println("mask=" + e.stateMask + ", char="
392: + e.character);
393: java.util.List keyStrokes = MacroUtil
394: .generatePossibleKeyStrokes(e);
395: if (keyStrokes.size() == 0)
396: return null;
397: for (int i = 0; i < keyStrokes.size(); i++) {
398: if (!((KeyStroke) keyStrokes.get(i)).isComplete())
399: return null;
400: }
401: System.out.println("keyStrokes=" + keyStrokes);
402: IWorkbenchCommandSupport csupport = PlatformUI.getWorkbench()
403: .getCommandSupport();
404: KeySequence keySequence = KeySequence.getInstance(keyStrokes);
405: System.out.println("keySequence=" + keySequence);
406: String commandId = csupport.getCommandManager()
407: .getPerfectMatch(keySequence);
408: System.out.println("Command id=" + commandId);
409: if (commandId == null)
410: return null;
411: return new KeyCommand(wid, commandId);
412: }
413:
414: private MacroCommand getLastCommand() {
415: if (commands.size() > 0) {
416: Object item = commands.get(commands.size() - 1);
417: if (item instanceof MacroCommand)
418: return (MacroCommand) item;
419: }
420: return null;
421: }
422:
423: public boolean isDisposed() {
424: return this .shell != null && this .shell.isDisposed();
425: }
426:
427: public void close() {
428: if (this .shell != null && !this .shell.isDisposed())
429: this .shell.close();
430: }
431:
432: public boolean tracks(Shell shell) {
433: if (this .shell != null && this .shell.equals(shell))
434: return true;
435: return false;
436: }
437:
438: public boolean playback(final Display display, Composite parent,
439: IProgressMonitor monitor) throws CoreException {
440: if (parent instanceof Shell) {
441: this .shell = (Shell) parent;
442: this .display = display;
443: hookWindow(true);
444: }
445:
446: NestedShell nestedShell = null;
447:
448: monitor.beginTask("", commands.size());
449:
450: for (int i = 0; i < commands.size(); i++) {
451: Object c = commands.get(i);
452: if (c instanceof MacroIndex) {
453: String id = ((MacroIndex) c).getId();
454: if (id != null && indexHandler != null) {
455: IStatus status = indexHandler.processIndex(shell,
456: id);
457: if (status.getSeverity() == IStatus.OK)
458: continue;
459: throw new CoreException(status);
460: }
461: // ignore the index
462: continue;
463: }
464: IPlayable playable = (IPlayable) c;
465: if (i < commands.size() - 1) {
466: // check the next command
467: IPlayable next = (IPlayable) commands.get(i + 1);
468: if (next instanceof MacroCommandShell) {
469: // this command will open a new shell
470: // add a listener before it is too late
471: MacroCommandShell nestedCommand = (MacroCommandShell) next;
472: nestedShell = new NestedShell(display,
473: nestedCommand, new SubProgressMonitor(
474: monitor, 1));
475: final NestedShell fnestedShell = nestedShell;
476: display.syncExec(new Runnable() {
477: public void run() {
478: display.addFilter(SWT.Activate,
479: fnestedShell);
480: }
481: });
482: }
483: }
484: if (playable instanceof MacroCommand) {
485: boolean last = i == commands.size() - 1;
486: playInGUIThread(display, playable, last, monitor);
487: monitor.worked(1);
488: } else if (nestedShell != null) {
489: CoreException e = null;
490: if (nestedShell.isReleased() == false) {
491: final NestedShell fnestedShell = nestedShell;
492: display.syncExec(new Runnable() {
493: public void run() {
494: display.removeFilter(SWT.Activate,
495: fnestedShell);
496: }
497: });
498: }
499: e = nestedShell.getException();
500: boolean result = nestedShell.getResult();
501: nestedShell = null;
502: if (e != null)
503: throw e;
504: if (!result)
505: return false;
506: }
507: }
508: shell = null;
509: return true;
510: }
511:
512: void addExistingIndices(ArrayList list) {
513: for (int i = 0; i < commands.size(); i++) {
514: Object c = commands.get(i);
515: if (c instanceof MacroIndex) {
516: list.add(((MacroIndex) c).getId());
517: } else if (c instanceof MacroCommandShell) {
518: ((MacroCommandShell) c).addExistingIndices(list);
519: }
520: }
521: }
522:
523: private void playInGUIThread(final Display display,
524: final IPlayable playable, boolean last,
525: final IProgressMonitor monitor) throws CoreException {
526: final CoreException[] ex = new CoreException[1];
527:
528: Runnable runnable = new Runnable() {
529: public void run() {
530: try {
531: //System.out.println("Executing: "+playable.toString());
532: playable.playback(display,
533: MacroCommandShell.this .shell, monitor);
534: MacroUtil.processDisplayEvents(display);
535: } catch (ClassCastException e) {
536: ex[0] = createPlaybackException(playable, e);
537: } catch (CoreException e) {
538: ex[0] = e;
539: } catch (SWTException e) {
540: ex[0] = createPlaybackException(playable, e);
541: } catch (SWTError error) {
542: ex[0] = createPlaybackException(playable, error);
543: }
544: }
545: };
546: // if (last)
547: // shell.getDisplay().asyncExec(runnable);
548: // else
549: // display.syncExec(runnable);
550: if (playable instanceof WaitCommand) {
551: playable.playback(display, this .shell, monitor);
552: } else
553: display.syncExec(runnable);
554:
555: try {
556: Thread.sleep(100);
557: } catch (InterruptedException e) {
558: }
559:
560: // for (;;) {
561: // if (display.isDisposed() || !display.readAndDispatch())
562: // break;
563: // }
564:
565: if (ex[0] != null)
566: throw ex[0];
567: }
568:
569: public IIndexHandler getIndexHandler() {
570: return indexHandler;
571: }
572:
573: public void setIndexHandler(IIndexHandler indexHandler) {
574: this .indexHandler = indexHandler;
575: for (int i = 0; i < commands.size(); i++) {
576: Object c = commands.get(i);
577: if (c instanceof MacroCommandShell) {
578: MacroCommandShell child = (MacroCommandShell) c;
579: child.setIndexHandler(indexHandler);
580: }
581: }
582: }
583:
584: private CoreException createPlaybackException(IPlayable playable,
585: Throwable th) {
586: IStatus status = new Status(IStatus.ERROR,
587: "org.eclipse.pde.ui.tests", IStatus.OK,
588: "Error while executing a macro command: "
589: + playable.toString(), th);
590: return new CoreException(status);
591: }
592: }
|