001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.netbeans.jellytools;
042:
043: import java.awt.Component;
044: import java.awt.Container;
045: import java.io.PrintStream;
046: import java.util.ArrayList;
047: import javax.swing.JComponent;
048: import javax.swing.JFrame;
049: import javax.swing.event.ChangeEvent;
050: import javax.swing.event.ChangeListener;
051:
052: import org.openide.awt.Toolbar;
053: import org.openide.awt.StatusDisplayer;
054: import org.openide.windows.WindowManager;
055:
056: import org.netbeans.jemmy.ComponentChooser;
057: import org.netbeans.jemmy.JemmyException;
058: import org.netbeans.jemmy.JemmyProperties;
059: import org.netbeans.jemmy.QueueTool;
060: import org.netbeans.jemmy.Waitable;
061: import org.netbeans.jemmy.Waiter;
062: import org.netbeans.jemmy.operators.ComponentOperator;
063: import org.netbeans.jemmy.operators.ContainerOperator;
064: import org.netbeans.jemmy.operators.JButtonOperator;
065: import org.netbeans.jemmy.operators.JFrameOperator;
066: import org.netbeans.jemmy.operators.JMenuBarOperator;
067: import org.netbeans.jemmy.operators.JPopupMenuOperator;
068: import org.netbeans.jemmy.util.NameComponentChooser;
069:
070: /**
071: * Handle NetBeans main window. It manipulates with toolbars and
072: * you can get text from status bar. To invoke menu items use
073: * {@link org.netbeans.jellytools.actions actions}.
074: * <br>
075: * It is singleton, so to get instance use static method <code>getDefault()</code>.
076: * <p>
077: * Usage:<br>
078: * <pre>
079: * MainWindowOperator mainWindow = MainWindowOperator.getDefault();
080: * mainWindow.waitStatusText("Finished");
081: * System.out.println("STATUS="+mainWindow.getStatusText());
082: * // push "Open" toolbar button in "System" toolbar
083: * mainWindow.getToolbarButton(mainWindow.getToolbar("System"), "Open").push();
084: * Thread.sleep(2000);
085: * new NbDialogOperator("Open").close();
086: * // invoke "Compile" menu item under "Build" menu
087: * new CompileAction().performMenu();
088: * // invoke About menu item under "Help" menu
089: * new ActionNoBlock("Help|About", null).perform();
090: * Thread.sleep(2000);
091: * new NbDialogOperator("About").close();
092: * </pre>
093: *
094: * @author Jiri.Skrivanek@sun.com
095: */
096: public class MainWindowOperator extends JFrameOperator {
097:
098: /** Singleton instance of MainWindowOperator. */
099: private static MainWindowOperator defaultMainWindowOperator;
100: /** JMenuBarOperator instance. */
101: private JMenuBarOperator _menuBar;
102: /** Instance of StatusTextTracer for this MainWindowOperator instance */
103: private static StatusTextTracer statusTextTracer = null;
104:
105: /** Creates new instance of MainWindowOperator. It gets instance of main window
106: * Frame.
107: */
108: protected MainWindowOperator() {
109: // run in dispatch thread
110: super ((JFrame) new QueueTool()
111: .invokeSmoothly(new QueueTool.QueueAction(
112: "getMainWindow") { // NOI18N
113: public Object launch() {
114: return WindowManager.getDefault()
115: .getMainWindow(); //NOI18N
116: }
117: }));
118: }
119:
120: /** Returns instance of MainWindowOperator. It is singleton, so this method
121: * is only way how to obtain instance. If instance not exists, it creates one.
122: * @return instance of MainWindowOperator
123: */
124: public static synchronized MainWindowOperator getDefault() {
125: if (defaultMainWindowOperator == null) {
126: defaultMainWindowOperator = new MainWindowOperator();
127: }
128: return defaultMainWindowOperator;
129: }
130:
131: /** Returns operator of main menu bar.
132: * @return JMenuBarOperator instance of main menu
133: */
134: public JMenuBarOperator menuBar() {
135: if (_menuBar == null) {
136: _menuBar = new JMenuBarOperator(this );
137: }
138: return _menuBar;
139: }
140:
141: /** Returns text from status bar.
142: * @return currently displayed text
143: */
144: public String getStatusText() {
145: return org.openide.awt.StatusDisplayer.getDefault()
146: .getStatusText();
147: }
148:
149: /** Sets given text to main window's status bar.
150: * @param newStatusText string to be displayed in status bar
151: */
152: public void setStatusText(String newStatusText) {
153: org.openide.awt.StatusDisplayer.getDefault().setStatusText(
154: newStatusText);
155: }
156:
157: /** Returns singleton instance of StatusTextTracer.
158: * @return singleton instance of StatusTextTracer
159: */
160: public synchronized StatusTextTracer getStatusTextTracer() {
161: if (statusTextTracer == null) {
162: statusTextTracer = new StatusTextTracer();
163: }
164: return statusTextTracer;
165: }
166:
167: /** Waits until given text appears in the main window status bar.
168: * If you want to trace status messages during an operation is proceed,
169: * use {@link StatusTextTracer}.
170: * @param text a text to wait for
171: */
172: public void waitStatusText(final String text) {
173: getStatusTextTracer().start();
174: try {
175: // not wait in case status text was already printed out
176: if (!getComparator().equals(getStatusText(), text)) {
177: getStatusTextTracer().waitText(text);
178: }
179: } finally {
180: getStatusTextTracer().stop();
181: }
182: }
183:
184: /***************** methods for toolbars manipulation *******************/
185:
186: /** Returns ContainerOperator representing index-th floating toolbar in
187: * IDE main window. Toolbars are NOT indexed from left to right.
188: * @param index index of toolbar to find
189: * @return ContainerOperator instance representing a toolbar
190: */
191: public ContainerOperator getToolbar(int index) {
192: ComponentChooser chooser = new ToolbarChooser();
193: return new ContainerOperator((Container) waitComponent(
194: (Container) getSource(), chooser, index));
195: }
196:
197: /** Returns ContainerOperator representing floating toolbar with given name.
198: * @param toolbarName toolbar's display name. It is shown in its tooltip.
199: * @return ContainerOperator instance representing a toolbar
200: */
201: public ContainerOperator getToolbar(String toolbarName) {
202: ComponentChooser chooser = new ToolbarChooser(toolbarName,
203: getComparator());
204: return new ContainerOperator((Container) waitComponent(
205: (Container) getSource(), chooser));
206: }
207:
208: /** Returns number of toolbars currently shown in IDE.
209: * @return number of toolbars
210: */
211: public int getToolbarCount() {
212: ToolbarChooser chooser = new ToolbarChooser(
213: "Non sense name - @#$%^&*", //NOI18N
214: getComparator());
215: findComponent((Container) getSource(), chooser);
216: return chooser.getCount();
217: }
218:
219: /** Returns display name of toolbar with given index. Toolbars are NOT
220: * indexed from left to right.
221: * @param index index of toolbar
222: * @return display name of toolbar
223: */
224: public String getToolbarName(int index) {
225: return ((Toolbar) getToolbar(index).getSource())
226: .getDisplayName();
227: }
228:
229: /** Return JButtonOperator representing a toolbar button found by given
230: * tooltip within given toolbar operator.
231: * @param toolbarOper ContainerOperator of a toolbar.
232: * Use {@link #getToolbar(String)} or {@link #getToolbar(int)}
233: * to obtain an operator.
234: * @param buttonTooltip tooltip of toolbar button
235: * @return JButtonOperator instance of found toolbar button
236: */
237: public JButtonOperator getToolbarButton(
238: ContainerOperator toolbarOper, String buttonTooltip) {
239: ToolbarButtonChooser chooser = new ToolbarButtonChooser(
240: buttonTooltip, getComparator());
241: return new JButtonOperator(JButtonOperator.waitJButton(
242: (Container) toolbarOper.getSource(), chooser));
243: }
244:
245: /** Return JButtonOperator representing index-th toolbar button within given
246: * toolbar operator.
247: * @param toolbarOper ContainerOperator of a toolbar.
248: * Use {@link #getToolbar(String)} or {@link #getToolbar(int)}
249: * to obtain an operator.
250: * @param index index of toolbar button to find
251: * @return JButtonOperator instance of found toolbar button
252: */
253: public JButtonOperator getToolbarButton(
254: ContainerOperator toolbarOper, int index) {
255: return new JButtonOperator(toolbarOper, index);
256: }
257:
258: /** Pushes popup menu on toolbars. It doesn't matter on which toolbar it is
259: * invoked, everytime it is the same. That's why popup menu is invoked on
260: * the right side of toolbar with index 0.
261: * @param popupPath path to menu item (e.g. "Edit")
262: */
263: public void pushToolbarPopupMenu(String popupPath) {
264: ContainerOperator contOper = getToolbar(0);
265: contOper.clickForPopup(contOper.getWidth() - 1, contOper
266: .getHeight() / 2);
267: new JPopupMenuOperator().pushMenu(popupPath, "|");
268: }
269:
270: /** Pushes popup menu on toolbars - no block further execution.
271: * It doesn't matter on which toolbar it is
272: * invoked, everytime it is the same. That's why popup menu is invoked on
273: * the right side of toolbar with index 0.
274: * @param popupPath path to menu item (e.g. "Save Configuration...")
275: */
276: public void pushToolbarPopupMenuNoBlock(String popupPath) {
277: ContainerOperator contOper = getToolbar(0);
278: contOper.clickForPopup(contOper.getWidth() - 1, contOper
279: .getHeight() / 2);
280: new JPopupMenuOperator().pushMenuNoBlock(popupPath, "|");
281: }
282:
283: /** Drags a toolbar to a new position determined by [x, y] relatively.
284: * @param toolbarOper ContainerOperator of a toolbar.
285: * Use {@link #getToolbar(String)} or {@link #getToolbar(int)}
286: * to obtain an operator.
287: * @param x relative move along x direction
288: * @param y relative move along y direction
289: */
290: public void dragNDropToolbar(ContainerOperator toolbarOper, int x,
291: int y) {
292: // find toolbar drag and drop area. It is a component named "grip"
293: Component comp = findComponent((Container) toolbarOper
294: .getSource(), new NameComponentChooser("grip"));
295: new ComponentOperator(comp).dragNDrop(comp.getWidth() / 2, comp
296: .getHeight() / 2, x, y);
297: }
298:
299: /** Chooser which can be used to find a org.openide.awt.Toolbar component or
300: * count a number of such components in given container.
301: */
302: private static class ToolbarChooser implements ComponentChooser {
303: private String toolbarName;
304: private StringComparator comparator;
305: private int count = 0;
306:
307: /** Use this to find org.openide.awt.Toolbar component with given name. */
308: public ToolbarChooser(String toolbarName,
309: StringComparator comparator) {
310: this .toolbarName = toolbarName;
311: this .comparator = comparator;
312: }
313:
314: /** Use this to count org.openide.awt.Toolbar components in given container. */
315: public ToolbarChooser() {
316: this .comparator = null;
317: }
318:
319: public boolean checkComponent(Component comp) {
320: if (comp instanceof org.openide.awt.Toolbar) {
321: count++;
322: if (comparator != null) {
323: return comparator.equals(((Toolbar) comp)
324: .getDisplayName(), toolbarName);
325: } else {
326: return true;
327: }
328: }
329: return false;
330: }
331:
332: public String getDescription() {
333: return "org.openide.awt.Toolbar";
334: }
335:
336: public int getCount() {
337: return count;
338: }
339: }
340:
341: /** Chooser which can be used to find a component with given tooltip,
342: * for example a toolbar button.
343: */
344: private static class ToolbarButtonChooser implements
345: ComponentChooser {
346: private String buttonTooltip;
347: private StringComparator comparator;
348:
349: public ToolbarButtonChooser(String buttonTooltip,
350: StringComparator comparator) {
351: this .buttonTooltip = buttonTooltip;
352: this .comparator = comparator;
353: }
354:
355: public boolean checkComponent(Component comp) {
356: return comparator.equals(((JComponent) comp)
357: .getToolTipText(), buttonTooltip);
358: }
359:
360: public String getDescription() {
361: return "Toolbar button with tooltip \"" + buttonTooltip
362: + "\".";
363: }
364: }
365:
366: /** Performs verification by accessing all sub-components */
367: public void verify() {
368: menuBar();
369: }
370:
371: /** Class to trace messages printed to status bar of the Main Window.
372: * <p>
373: * Usage:<br>
374: * <pre>
375: * MainWindowOperator.StatusTextTracer stt = MainWindowOperator.getDefault().getStatusTextTracer();
376: * // start tracing
377: * stt.start();
378: * // compile action will produce at least two messages: "Compiling ...",
379: * // "Finished ..."
380: * new CompileAction().performAPI();
381: *
382: * // waits for "Compiling" status text
383: * stt.waitText("Compiling");
384: * // waits for "Finished" status text
385: * stt.waitText("Finished");
386: *
387: * // order is not significant => following works as well
388: * stt.waitText("Finished");
389: * stt.waitText("Compiling");
390: *
391: * // to be order significant, set removedCompared parameter to true
392: * stt.waitText("Compiling", true);
393: * stt.waitText("Finished", true);
394: *
395: * // history was removed by above methods => need to produce a new messages
396: * new CompileAction().performAPI();
397: *
398: * // order is significant if removedCompared parameter is true =>
399: * // => following fails because Finished is shown as second
400: * stt.waitText("Finished", true);
401: * stt.waitText("Compiling", true);
402: *
403: * // stop tracing
404: * stt.stop();
405: * </pre>
406: */
407: public class StatusTextTracer implements ChangeListener {
408: /** List of all messages */
409: private ArrayList<String> statusTextHistory;
410:
411: /** Creates new instance. */
412: public StatusTextTracer() {
413: this .statusTextHistory = new ArrayList<String>();
414: }
415:
416: /** Starts to register all status messages into history array.
417: * Exactly, it adds the listener to org.openide.awt.StatusDisplayer.
418: * It clears possible previously filled history array before.
419: */
420: public void start() {
421: stop();
422: clear();
423: StatusDisplayer.getDefault().addChangeListener(this );
424: }
425:
426: /** Stops registering of status messages. Exactly, it removes the
427: * listener from org.openide.awt.StatusDisplayer.
428: */
429: public void stop() {
430: StatusDisplayer.getDefault().removeChangeListener(this );
431: }
432:
433: /** Called when status text was changed. It adds status text to history
434: * array.
435: * @param evt change event - not used
436: */
437: public void stateChanged(ChangeEvent evt) {
438: synchronized (this ) {
439: statusTextHistory.add(StatusDisplayer.getDefault()
440: .getStatusText());
441: // print message to jemmy output stream
442: JemmyProperties.getCurrentOutput().printTrace(
443: "Status text changed to: \""
444: + StatusDisplayer.getDefault()
445: .getStatusText() + "\"");
446: }
447: }
448:
449: /** Clears status text history array. */
450: public void clear() {
451: synchronized (this ) {
452: statusTextHistory.clear();
453: }
454: }
455:
456: /** Checks whether given text equals to any of messages in the history
457: * array. Comparator of this MainWindowOperator instance is used.
458: * If <tt>removeCompared</tt> parameter is set to <tt>true</tt>,
459: * messages already compared are removed from history array. Otherwise
460: * messages are not removed until {@link #clear} or {@link #start} are
461: * called.
462: * @param text a text to be compared
463: * @param removeCompared whether to remove already compared messages from
464: * history array
465: * @return true if text matches any of messages in the history array;
466: * false otherwise
467: */
468: public boolean contains(String text, boolean removeCompared) {
469: StringComparator comparator = getComparator();
470: synchronized (this ) {
471: if (removeCompared) {
472: while (!statusTextHistory.isEmpty()) {
473: String status = (String) statusTextHistory
474: .remove(0);
475: if (comparator.equals(status, text)) {
476: return true;
477: }
478: }
479: } else {
480: for (int i = 0; i < statusTextHistory.size(); i++) {
481: if (comparator
482: .equals((String) statusTextHistory
483: .get(i), text)) {
484: return true;
485: }
486: }
487: }
488: return false;
489: }
490: }
491:
492: /** Waits for text to be shown in the Main Window status bar not
493: * removing any message from history.
494: * Comparator of this MainWindowOperator instance is used.
495: * It throws TimeoutExpiredException if timeout expires.
496: * @param text a text to wait for
497: */
498: public void waitText(final String text) {
499: waitText(text, false);
500: }
501:
502: /** Waits for text to be shown in the Main Window status bar.
503: * Comparator of this MainWindowOperator instance is used.
504: * If <tt>removeCompared</tt> parameter is set to <tt>true</tt>,
505: * messages already compared are removed from history array. It satisfies
506: * that order of messages is significant when this method is called
507: * more than once.
508: * If <tt>removeCompared</tt> parameter is set to <tt>false</tt>,
509: * messages are not removed until {@link #clear} or {@link #start} are
510: * called and its order is not taken into account.
511: * @param text a text to wait for
512: * @param removeCompared whether to remove already compared messages from
513: * history array
514: */
515: public void waitText(final String text,
516: final boolean removeCompared) {
517: try {
518: new Waiter(new Waitable() {
519: public Object actionProduced(Object anObject) {
520: return contains(text, removeCompared) ? Boolean.TRUE
521: : null;
522: }
523:
524: public String getDescription() {
525: return ("Wait status text equals to " + text);
526: }
527: }).waitAction(null);
528: } catch (InterruptedException e) {
529: throw new JemmyException("Interrupted.", e);
530: }
531: }
532:
533: /** Calls {@link #stop} at the end of life cycle of this class. */
534: public void finalize() {
535: stop();
536: }
537:
538: /** Returns list of elements collected from the moment method
539: * {@link #start} was called. Remember, if <tt>removeCompared</tt>
540: * parameter is set to <tt>true</tt> in some of methods,
541: * messages already compared are removed from history array.
542: * @return ArrayList of elements representing status text messages
543: */
544: public ArrayList getStatusTextHistory() {
545: return statusTextHistory;
546: }
547:
548: /** Prints list of elements collected from the moment method
549: * {@link #start} was called. Remember, if <tt>removeCompared</tt>
550: * parameter is set to <tt>true</tt> in some of methods,
551: * messages already compared are removed from history array.
552: * @param outputPrintStream stream to print output in
553: */
554: public void printStatusTextHistory(PrintStream outputPrintStream) {
555: for (int i = 0; i < statusTextHistory.size(); i++) {
556: outputPrintStream.println(statusTextHistory.get(i)
557: .toString());
558: }
559: }
560: }
561: }
|