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-2006 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:
042: package org.netbeans.jellytools;
043:
044: import java.awt.Component;
045: import java.awt.event.InputEvent;
046: import java.awt.event.KeyEvent;
047: import java.lang.reflect.Method;
048: import javax.swing.JComponent;
049: import javax.swing.JTabbedPane;
050: import javax.swing.KeyStroke;
051: import javax.swing.text.Document;
052: import org.netbeans.core.output2.ui.AbstractOutputTab;
053: import org.netbeans.jellytools.actions.Action;
054: import org.netbeans.jellytools.actions.CopyAction;
055: import org.netbeans.jellytools.actions.FindAction;
056: import org.netbeans.jellytools.actions.ActionNoBlock;
057: import org.netbeans.jemmy.ComponentChooser;
058: import org.netbeans.jemmy.JemmyException;
059: import org.netbeans.jemmy.operators.ComponentOperator;
060: import org.netbeans.jemmy.operators.JComponentOperator;
061: import org.netbeans.jemmy.operators.JTabbedPaneOperator;
062: import org.netbeans.jemmy.operators.Operator;
063:
064: /** Operator for Output tab. It resides in output top component.
065: * <p>
066: * Usage:<br>
067: * <pre>
068: * // find output tab with given name
069: * OutputTabOperator oto = new OutputTabOperator("compile-single");
070: * // wait for a message appears in output
071: * oto.waitText("my message");
072: * // get the text
073: * String wholeOutput = oto.getText();
074: * // close this output
075: * oto.close();
076: * </pre>
077: *
078: * @author Jiri.Skrivanek@sun.com
079: * @see OutputOperator
080: */
081: public class OutputTabOperator extends JComponentOperator {
082:
083: // operator of OutputPane component
084: ComponentOperator outputPaneOperator;
085:
086: // actions used only in OutputTabOperator
087: private static final Action findNextAction = new Action(null,
088: Bundle.getString("org.netbeans.core.output2.Bundle",
089: "ACTION_FIND_NEXT"), null, KeyStroke.getKeyStroke(
090: KeyEvent.VK_F3, 0));
091:
092: private static final Action selectAllAction = new Action(null,
093: null, null, System.getProperty("os.name").toLowerCase()
094: .indexOf("mac") > -1 ? KeyStroke.getKeyStroke(
095: KeyEvent.VK_A, KeyEvent.META_MASK) : KeyStroke
096: .getKeyStroke(KeyEvent.VK_A, KeyEvent.CTRL_MASK));
097:
098: private static final Action wrapTextAction = new Action(null,
099: Bundle.getString("org.netbeans.core.output2.Bundle",
100: "ACTION_WRAP"), null, System.getProperty("os.name")
101: .toLowerCase().indexOf("mac") > -1 ? KeyStroke
102: .getKeyStroke(KeyEvent.VK_W, KeyEvent.META_MASK)
103: : KeyStroke.getKeyStroke(KeyEvent.VK_W,
104: KeyEvent.CTRL_MASK));
105:
106: private static final ActionNoBlock saveAsAction = new ActionNoBlock(
107: null,
108: Bundle.getString("org.netbeans.core.output2.Bundle",
109: "ACTION_SAVEAS"),
110: null,
111: System.getProperty("os.name").toLowerCase().indexOf("mac") > -1 ? KeyStroke
112: .getKeyStroke(KeyEvent.VK_S, KeyEvent.META_MASK)
113: : KeyStroke.getKeyStroke(KeyEvent.VK_S,
114: KeyEvent.CTRL_MASK));
115:
116: private static final Action nextErrorAction = new Action(null,
117: null, null, KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0));
118:
119: private static final Action previousErrorAction = new Action(null,
120: null, null, KeyStroke.getKeyStroke(KeyEvent.VK_F12,
121: InputEvent.SHIFT_MASK));
122:
123: private static final Action closeAction = new Action(
124: null,
125: Bundle.getString("org.netbeans.core.output2.Bundle",
126: "ACTION_CLOSE"),
127: null,
128: System.getProperty("os.name").toLowerCase().indexOf("mac") > -1 ? KeyStroke
129: .getKeyStroke(KeyEvent.VK_F4, KeyEvent.META_MASK)
130: : KeyStroke.getKeyStroke(KeyEvent.VK_F4,
131: KeyEvent.CTRL_MASK));
132:
133: private static final Action clearAction = new Action(
134: null,
135: Bundle.getString("org.netbeans.core.output2.Bundle",
136: "ACTION_CLEAR"),
137: null,
138: System.getProperty("os.name").toLowerCase().indexOf("mac") > -1 ? KeyStroke
139: .getKeyStroke(KeyEvent.VK_L, KeyEvent.META_MASK)
140: : KeyStroke.getKeyStroke(KeyEvent.VK_L,
141: KeyEvent.CTRL_MASK));
142:
143: private static final CopyAction copyAction = new CopyAction();
144: private static final FindAction findAction = new FindAction();
145:
146: /** Create new instance of OutputTabOperator from given component.
147: * @param source JComponent source
148: */
149: public OutputTabOperator(JComponent source) {
150: // used in OutputOperator
151: super (source);
152: }
153:
154: /** Waits for output tab with given name.
155: * It is activated by defalt.
156: * @param name name of output tab to look for
157: */
158: public OutputTabOperator(String name) {
159: this (name, 0);
160: }
161:
162: /** Waits for index-th output tab with given name.
163: * It is activated by defalt.
164: * @param name name of output tab to look for
165: * @param index index of requested output tab with given name
166: */
167: public OutputTabOperator(String name, int index) {
168: super ((JComponent) new OutputOperator().waitSubComponent(
169: new OutputTabSubchooser(name), index));
170: makeComponentVisible();
171: }
172:
173: /** Activates this output tab. If this output tab is in tabbed pane, it is selected. If
174: * it is only tab in the Output top component, the Output top component
175: * is activated.
176: */
177: public void makeComponentVisible() {
178: if (getParent() instanceof JTabbedPane) {
179: super .makeComponentVisible();
180: // output tab is a tab of JTabbedPane
181: new JTabbedPaneOperator((JTabbedPane) getParent())
182: .setSelectedComponent(getSource());
183: } else {
184: // output tab is sub component of Output top component
185: new OutputOperator().makeComponentVisible();
186: }
187: }
188:
189: /** Returns length of written text. It is a number of written characters.
190: * @return length of already written text
191: */
192: public int getLength() {
193: // ((OutputTab)getSource()).getDocument().getLength();
194: return runMapping(new MapIntegerAction("getLength") {
195: public int map() {
196: Document document = ((AbstractOutputTab) getSource())
197: .getOutputPane().getDocument();
198: try {
199: Class clazz = Class
200: .forName("org.netbeans.core.output2.OutputDocument");
201: Method getLengthMethod = clazz.getDeclaredMethod(
202: "getLength", (Class[]) null);
203: getLengthMethod.setAccessible(true);
204: return ((Integer) getLengthMethod.invoke(document,
205: (Object[]) null)).intValue();
206: } catch (Exception e) {
207: throw new JemmyException(
208: "getLength() by reflection failed.", e);
209: }
210: }
211: });
212: }
213:
214: /** Finds a line number by text.
215: * @param lineText String line text
216: * @return line number of specified text starting at 0; -1 if text not found
217: */
218: public int findLine(String lineText) {
219: int lineCount = getLineCount();
220: if (lineCount < 1) {
221: // no line yet
222: return -1;
223: }
224: for (int i = 0; i < lineCount; i++) {
225: if (getComparator().equals(getLine(i), lineText)) {
226: return i;
227: }
228: }
229: return -1;
230: }
231:
232: /** Returns text from this output tab.
233: * @return text from this output tab.
234: */
235: public String getText() {
236: final int length = getLength();
237: return (String) runMapping(new MapAction("getText") {
238: public Object map() {
239: Document document = ((AbstractOutputTab) getSource())
240: .getOutputPane().getDocument();
241: try {
242: Class clazz = Class
243: .forName("org.netbeans.core.output2.OutputDocument");
244: Method getTextMethod = clazz.getDeclaredMethod(
245: "getText", new Class[] { int.class,
246: int.class });
247: getTextMethod.setAccessible(true);
248: return getTextMethod.invoke(
249: document,
250: new Object[] { new Integer(0),
251: new Integer(length) }).toString();
252: } catch (Exception e) {
253: throw new JemmyException(
254: "Getting text by reflection failed.", e);
255: }
256: }
257: });
258: }
259:
260: /** Get text between <code>startLine</code> and <code>endLine</code> from this output tab.
261: * Both <code>startLine</code> and <code>endLine</code> are included.
262: * @param startLine first line to be included (starting at 0)
263: * @param endLine last line to be included
264: * @return text between <code>startLine</code> and <code>endLine</code> from this output tab
265: */
266: public String getText(int startLine, int endLine) {
267: StringBuffer result = new StringBuffer();
268: for (int i = startLine; i <= endLine; i++) {
269: result.append(getLine(i));
270: result.append('\n');
271: ;
272: }
273: return result.toString();
274: }
275:
276: /** Waits for text to be displayed in this output tab.
277: * @param text text to wait for
278: */
279: public void waitText(final String text) {
280: getOutput().printLine(
281: "Wait \"" + text + "\" text in component \n : "
282: + toStringSource());
283: getOutput().printGolden("Wait \"" + text + "\" text");
284: waitState(new ComponentChooser() {
285: public boolean checkComponent(Component comp) {
286: return (findLine(text) > -1);
287: }
288:
289: public String getDescription() {
290: return ("\"" + text + "\" text");
291: }
292: });
293: }
294:
295: /** Returns count of filled lines of this output tab.
296: * @return count of filled lines of this output tab.
297: */
298: public int getLineCount() {
299: return ((Integer) runMapping(new MapAction("getLineCount") {
300: public Object map() {
301: Document document = ((AbstractOutputTab) getSource())
302: .getOutputPane().getDocument();
303: try {
304: Class clazz = Class
305: .forName("org.netbeans.core.output2.OutputDocument");
306: Method getElementCountMethod = clazz
307: .getDeclaredMethod("getElementCount",
308: (Class[]) null);
309: getElementCountMethod.setAccessible(true);
310: return (Integer) getElementCountMethod.invoke(
311: document, (Object[]) null);
312: } catch (Exception e) {
313: throw new JemmyException(
314: "getElementCount() by reflection failed.",
315: e);
316: }
317: }
318: })).intValue();
319: }
320:
321: /** Returns operator for OutputPane component.
322: * All events should be dispatched to this component.
323: * @return operator for OutputPane component
324: */
325: public ComponentOperator outputPaneOperator() {
326: // first make component visible because tab must be visible to dispatch events
327: makeComponentVisible();
328: if (outputPaneOperator == null) {
329: outputPaneOperator = ComponentOperator
330: .createOperator(((AbstractOutputTab) getSource())
331: .getOutputPane());
332: outputPaneOperator.copyEnvironment(this );
333: }
334: return outputPaneOperator;
335: }
336:
337: /** Returns text from specified line.
338: * @param line line number to get text from
339: * @return text from the specified line (starting at 0)
340: */
341: public String getLine(final int line) {
342: return (String) runMapping(new MapAction("getText") {
343: public Object map() {
344: Document document = ((AbstractOutputTab) getSource())
345: .getOutputPane().getDocument();
346: try {
347: Class clazz = Class
348: .forName("org.netbeans.core.output2.OutputDocument");
349: Method getLineStartMethod = clazz
350: .getDeclaredMethod("getLineStart",
351: new Class[] { int.class });
352: getLineStartMethod.setAccessible(true);
353: Integer lineStart = (Integer) getLineStartMethod
354: .invoke(document,
355: new Object[] { new Integer(line) });
356: Method getLineEndMethod = clazz.getDeclaredMethod(
357: "getLineEnd", new Class[] { int.class });
358: getLineEndMethod.setAccessible(true);
359: Integer lineEnd = (Integer) getLineEndMethod
360: .invoke(document,
361: new Object[] { new Integer(line) });
362: if (lineStart.intValue() == lineEnd.intValue()) {
363: // line is empty
364: return "";
365: }
366: Method getTextMethod = clazz.getDeclaredMethod(
367: "getText", new Class[] { int.class,
368: int.class });
369: getTextMethod.setAccessible(true);
370: return getTextMethod
371: .invoke(
372: document,
373: new Object[] {
374: lineStart,
375: new Integer(lineEnd
376: .intValue()
377: - lineStart
378: .intValue()
379: - 1) }).toString();
380: } catch (Exception e) {
381: throw new JemmyException(
382: "Getting text by reflection failed.", e);
383: }
384: }
385: });
386: }
387:
388: /** SubChooser to determine OutputTab component
389: * Used in findTopComponent method.
390: */
391: protected static final class OutputTabSubchooser implements
392: ComponentChooser {
393:
394: /** Name of OutputTab to search for. */
395: private String tabName;
396:
397: public OutputTabSubchooser() {
398: }
399:
400: public OutputTabSubchooser(String tabName) {
401: this .tabName = tabName;
402: }
403:
404: public boolean checkComponent(Component comp) {
405: if (comp.getClass().getName().endsWith("OutputTab")) { // NOI18N
406: return Operator.getDefaultStringComparator().equals(
407: comp.getName(), tabName);
408: } else {
409: return false;
410: }
411: }
412:
413: public String getDescription() {
414: return "org.netbeans.core.output2.OutputTab"; // NOI18N
415: }
416: }
417:
418: /** Performs verification by accessing all sub-components */
419: public void verify() {
420: outputPaneOperator();
421: }
422:
423: /****************************** Actions *****************************/
424:
425: /** Performs copy action. */
426: public void copy() {
427: copyAction.perform(outputPaneOperator());
428: }
429:
430: /** Performs find action. */
431: public void find() {
432: findAction.perform(outputPaneOperator());
433: }
434:
435: /** Performs find next action. */
436: public void findNext() {
437: findNextAction.perform(outputPaneOperator());
438: }
439:
440: /** Performs next error action. */
441: public void nextError() {
442: nextErrorAction.perform(outputPaneOperator());
443: }
444:
445: /** Performs next error action. */
446: public void previousError() {
447: previousErrorAction.perform(outputPaneOperator());
448: }
449:
450: /** Performs wrap text action. */
451: public void wrapText() {
452: wrapTextAction.perform(outputPaneOperator());
453: }
454:
455: /** Performs save as action. */
456: public void saveAs() {
457: saveAsAction.perform(outputPaneOperator());
458: }
459:
460: /** Performs close action. */
461: public void close() {
462: closeAction.perform(outputPaneOperator());
463: }
464:
465: /** Performs clear action. */
466: public void clear() {
467: clearAction.perform(outputPaneOperator());
468: }
469:
470: /** Performs select all action. */
471: public void selectAll() {
472: selectAllAction.perform(outputPaneOperator());
473: }
474: }
|