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:
042: package org.netbeans.modules.visualweb.gravy;
043:
044: import java.awt.Component;
045: import java.awt.Container;
046: import java.awt.event.KeyEvent;
047: import java.lang.reflect.Method;
048: import java.util.ArrayList;
049: import java.util.Iterator;
050: import java.util.List;
051: import javax.swing.JComponent;
052: import javax.swing.JToolBar;
053: import javax.swing.text.BadLocationException;
054: import javax.swing.text.Document;
055: import javax.swing.text.StyledDocument;
056: import org.netbeans.modules.visualweb.gravy.actions.SaveAction;
057: import org.netbeans.modules.visualweb.gravy.TopComponentOperator;
058:
059: import org.netbeans.jemmy.*;
060: import org.netbeans.jemmy.operators.*;
061:
062: import org.openide.cookies.LineCookie;
063: import org.openide.loaders.DataObject;
064: import org.openide.text.Annotatable;
065: import org.openide.text.Annotation;
066: import org.openide.text.CloneableEditor;
067: import org.openide.text.Line;
068: import org.openide.text.NbDocument;
069: import org.openide.text.Line.Set;
070: import org.openide.windows.TopComponent;
071:
072: /**
073: * Handle an editor top component in NetBeans IDE. It enables to get, select, insert or
074: * delete text, move caret, work with annotations and with toolbar buttons.
075: * Majority of operations is done by JEditorPane API calls. If you want
076: * to do operations by key navigation, use methods of JEditorPaneOperator
077: * instance by {@link #txtEditorPane()}. For example, call
078: * <code>txtEditorPane().changeCaretPosition(int)</code> instead of
079: * <code>{@link #setCaretPosition(int)}</code>.
080: * <p>
081: * Usage:<br>
082: * <pre>
083: EditorOperator eo = new EditorOperator(filename);
084: eo.setCaretPositionToLine(10);
085: eo.insert("// My new comment\n");
086: eo.select("// My new comment");
087: eo.deleteLine(10);
088: eo.getToolbarButton("Toggle Bookmark").push();
089: // discard changes and close
090: eo.close(false);
091: // save changes and close
092: eo.close(true);
093: // try to close all opened documents (confirmation dialog may appear)
094: eo.closeAllDocuments();
095: // close all opened documents and discard all changes
096: eo.closeDiscardAll();
097: * </pre>
098: *
099: * @author Jiri.Skrivanek@sun.com
100: */
101: public class EditorOperator extends TopComponentOperator {
102:
103: private static int WAIT_TIME = 60000;
104:
105: static {
106: Timeouts.initDefault("EditorOperator.WaitModifiedTimeout",
107: WAIT_TIME);
108: }
109:
110: /** Components operators. */
111: private JEditorPaneOperator _txtEditorPane;
112: private JLabelOperator _lblRowColumn;
113: private JLabelOperator _lblInputMode;
114: private JLabelOperator _lblStatusBar;
115: private JComboBoxOperator _cboQuickBrowse;
116:
117: /** Waits for the first opened editor with given name.
118: * If not active, it is activated.
119: * @param filename name of file showed in the editor (it used to be label of tab)
120: */
121: public EditorOperator(String filename) {
122: this (filename, 0);
123: }
124:
125: /** Waits for index-th opened editor with given name.
126: * If not active, it is activated.
127: * @param filename name of file showed in the editor (it used to be label of tab)
128: * @param index index of editor to be find
129: */
130: public EditorOperator(String filename, int index) {
131: super (waitTopComponent(null, filename, index,
132: new EditorSubchooser(filename)));
133: // super(filename, new EditorSubchooser());
134: this .requestFocus(); // needed for pushKey() methods
135: }
136:
137: /* public EditorOperator(ContainerOperator contOper) {
138: super(contOper, new EditorSubchooser());
139: this.requestFocus(); // needed for pushKey() methods
140: }
141: */
142: /** Waits for first open editor with given name in specified container.
143: * If not active, it is activated.
144: * @param contOper container where to search
145: * @param filename name of file showed in the editor (it used to be label of tab)
146: */
147: public EditorOperator(ContainerOperator contOper, String filename) {
148: // this(contOper, filename, 0);
149: super (contOper, new EditorSubchooser(filename));
150: this .requestFocus(); // needed for pushKey() methods
151:
152: }
153:
154: /** Waits for index-th opened editor with given name in specified container.
155: * If not active, it is activated.
156: * @param contOper container where to search
157: * @param filename name of file showed in the editor (it used to be label of tab)
158: * @param index index of editor to be find
159: */
160: public EditorOperator(ContainerOperator contOper, String filename,
161: int index) {
162: super (waitTopComponent(contOper, filename, index,
163: new EditorSubchooser(filename)));
164: copyEnvironment(contOper);
165: this .requestFocus(); // needed for pushKey() methods
166: }
167:
168: /** Closes all opened documents and discards all changes by IDE API calls.
169: * It works also if no file is modified, so it is a safe way how to close
170: * documents and no block further execution.
171: */
172: public static void closeDiscardAll() {
173: // run in dispatch thread
174: // TODO: workaround for compilation
175: JTabbedPaneOperator tabbed = new JTabbedPaneOperator(
176: RaveWindowOperator.getDefault());
177: for (int i = 0; i < tabbed.getTabCount(); i++) {
178: EditorOperator.close((TopComponent) tabbed.getComponent(i),
179: false);
180: }
181: /* TODO: doesn't work; rewrite it
182: ModeImpl mode = (ModeImpl)new QueueTool().invokeSmoothly(new QueueTool.QueueAction("findMode") { // NOI18N
183: public Object launch() {
184: return WindowManagerImpl.getInstance().findMode("editor"); //NOI18N
185: }
186: });
187: Iterator iter = mode.getOpenedTopComponents().iterator();
188:
189: while(iter.hasNext()) {
190: EditorOperator.close((TopComponent)iter.next(), false);
191: }
192: */
193: }
194:
195: /** Closes this editor by IDE API call and depending on given flag
196: * it saves or discards changes.
197: * @param save true - save changes, false - discard changes
198: */
199: public void close(boolean save) {
200: if (save) {
201: super .save();
202: close();
203: } else {
204: closeDiscard();
205: }
206: }
207:
208: /** Closes top component. It saves it or not depending on given flag.
209: * Other top components like VCS outputs are closed directly.
210: * It is package private because it is also used by EditorWindowOperator.
211: */
212: static void close(TopComponent tc, boolean save) {
213: TopComponentOperator tco = new TopComponentOperator(tc);
214: if (save) {
215: tco.save();
216: tco.close();
217: } else {
218: tco.closeDiscard();
219: }
220: }
221:
222: /** Returns operator of currently shown editor pane.
223: * @return JTabbedPaneOperator instance of editor pane
224: */
225: public JEditorPaneOperator txtEditorPane() {
226: if (_txtEditorPane == null) {
227: _txtEditorPane = new JEditorPaneOperator(this );
228: }
229: return _txtEditorPane;
230: }
231:
232: /** Returns operator of label showing current row and column at the left
233: * corner of the Source Editor window.
234: * @return JLabelOperator instance of row:column label
235: */
236: public JLabelOperator lblRowColumn() {
237: if (_lblRowColumn == null) {
238: _lblRowColumn = new JLabelOperator(this , 0);
239: }
240: return _lblRowColumn;
241: }
242:
243: /** Returns operator of label showing current input mode (INS/OVR -
244: * insert/overwrite).
245: * @return JLabelOperator instance of input mode label
246: */
247: public JLabelOperator lblInputMode() {
248: if (_lblInputMode == null) {
249: _lblInputMode = new JLabelOperator(this , 1);
250: }
251: return _lblInputMode;
252: }
253:
254: /** Returns operator of status bar at the bottom of the Source Editor.
255: * @return JLabelOperator instance of status bar
256: */
257: public JLabelOperator lblStatusBar() {
258: if (_lblStatusBar == null) {
259: _lblStatusBar = new JLabelOperator(this , 2);
260: }
261: return _lblStatusBar;
262: }
263:
264: /** Returns operator of combo box showing members of the class. It
265: * is applicable only for Java objects.
266: * @return JComboBoxOperator instance of members combo box
267: */
268: public JComboBoxOperator cboQuickBrowse() {
269: if (_cboQuickBrowse == null) {
270: _cboQuickBrowse = new JComboBoxOperator(this );
271: }
272: return _cboQuickBrowse;
273: }
274:
275: /** Selects item in quick browse combo box.
276: * @param item itme to be selected
277: */
278: public void setQuickBrowse(String item) {
279: cboQuickBrowse().selectItem(item);
280: }
281:
282: /************** Get, select, delete, insert text ************************/
283:
284: /** Gets text from the currently opened Editor window.
285: * @return a string representing whole content of the Editor window
286: * (including new line characters)
287: */
288: public String getText() {
289: return txtEditorPane().getText();
290: }
291:
292: /** Gets text from specified line.
293: * It might fail on the last line of a file because of issues
294: * http://www.netbeans.org/issues/show_bug.cgi?id=24434 and
295: * http://www.netbeans.org/issues/show_bug.cgi?id=24433.
296: * @param lineNumber number of line (beggining from 1)
297: * @return a string representing content of the line including new line
298: * character
299: */
300: public String getText(int lineNumber) {
301: return ((Line) getLine(lineNumber)).getText();
302: }
303:
304: /** Returns instance of org.openide.text.Line for given line number.
305: * @param lineNumber number of line (beggining at 1)
306: * @return org.openide.text.Line instance
307: */
308: private Object getLine(int lineNumber) {
309: Document doc = txtEditorPane().getDocument();
310: DataObject od = (DataObject) doc
311: .getProperty(Document.StreamDescriptionProperty);
312: Set set = ((LineCookie) od.getCookie(LineCookie.class))
313: .getLineSet();
314: try {
315: return set.getCurrent(lineNumber - 1);
316: } catch (IndexOutOfBoundsException e) {
317: throw new JemmyException("Index must be > 0", e);
318: }
319: }
320:
321: /** Checks if editor window contains text specified as parameter text.
322: * @param text text to compare to
323: * @return true if text was found, false otherwise
324: */
325: public boolean contains(String text) {
326: return getText().indexOf(text) != -1;
327: }
328:
329: /** Selects whole line specified by its number. Caret will stand at the
330: * next available line.
331: * @param lineNumber number of line (beggining from 1)
332: */
333: public void select(int lineNumber) {
334: int lineOffset = getLineOffset(lineNumber);
335: setCaretPosition(lineOffset);
336: txtEditorPane().moveCaretPosition(
337: lineOffset + getText(lineNumber).length());
338: }
339:
340: /** Selects text between line1 and line2 (both are included). Caret will
341: * stand behing the selection (at the next line if available).
342: * @param line1 number of line where to begin (beggining from 1)
343: * @param line2 number of line where to finish (beggining from 1)
344: */
345: public void select(int line1, int line2) {
346: setCaretPosition(getLineOffset(line1));
347: txtEditorPane().moveCaretPosition(
348: getLineOffset(line2) + getText(line2).length());
349: }
350:
351: /** Selects text in specified line on position defined by column1
352: * and column2 (both are included). Caret will stand at the end of
353: * the selection.
354: * @param lineNumber number of line (beggining from 1)
355: * @param column1 column position where selection starts (beggining from 1)
356: * @param column2 column position where selection ends (beggining from 1) */
357: public void select(int lineNumber, int column1, int column2) {
358: int lineOffset = getLineOffset(lineNumber);
359: setCaretPosition(lineOffset + column1 - 1);
360: txtEditorPane().moveCaretPosition(lineOffset + column2);
361: }
362:
363: /** Selects index-th occurence of given text.
364: * @param text text to be selected
365: * @param index index of text occurence (first occurence has index 0)
366: * @see #select(String)
367: */
368: public void select(String text, int index) {
369: int position = txtEditorPane().getPositionByText(text, index);
370: if (position == -1) {
371: throw new JemmyException(index + "-th occurence of \""
372: + text + "\" not found.");
373: }
374: setCaretPosition(position);
375: txtEditorPane().moveCaretPosition(position + text.length());
376: }
377:
378: /** Selects first occurence of given text.
379: * @param text text to be selected
380: * @see #select(String, int)
381: */
382: public void select(String text) {
383: select(text, 0);
384: }
385:
386: /** Replaces first occurence of oldText by newText.
387: * @param oldText text to be replaced
388: * @param newText text to write instead
389: */
390: public void replace(String oldText, String newText) {
391: replace(oldText, newText, 0);
392: }
393:
394: /** Replaced index-th occurence of oldText by newText.
395: * @param oldText text to be replaced
396: * @param newText text to write instead
397: * @param index index of oldText occurence (first occurence has index 0)
398: */
399: public void replace(String oldText, String newText, int index) {
400: select(oldText, index);
401: txtEditorPane().replaceSelection(newText);
402: }
403:
404: /** Inserts text to current position. Caret will stand at the end
405: * of newly inserted text.
406: * @param text a string to be inserted
407: */
408: public void insert(final String text) {
409: final int offset = txtEditorPane().getCaretPosition();
410: runMapping(new MapVoidAction("insertString") {
411: public void map() {
412: try {
413: txtEditorPane().getDocument().insertString(offset,
414: text, null);
415: } catch (BadLocationException e) {
416: throw new JemmyException("Cannot insert \"" + text
417: + "\" to position " + offset + ".", e);
418: }
419: }
420: });
421: }
422:
423: /** Inserts text to position specified by line number and column.
424: * Caret will stand at the end of newly inserted text.
425: * @param text a string to be inserted
426: * @param lineNumber number of line (beggining from 1)
427: * @param column column position (beggining from 1)
428: */
429: public void insert(String text, int lineNumber, int column) {
430: setCaretPosition(lineNumber, column);
431: insert(text);
432: }
433:
434: /** Deletes given number of characters from specified possition.
435: * Position of caret will not change.
436: * @param offset position inside document (0 means the beginning)
437: * @param length number of characters to be deleted
438: */
439: public void delete(int offset, int length) {
440: try {
441: txtEditorPane().getDocument().remove(offset, length);
442: } catch (BadLocationException e) {
443: throw new JemmyException("Cannot delete " + length
444: + " characters from position " + offset + ".", e);
445: }
446: }
447:
448: /** Deletes given number of characters from current caret possition.
449: * Position of caret will not change.
450: * @param length number of characters to be deleted
451: */
452: public void delete(int length) {
453: delete(txtEditorPane().getCaretPosition(), length);
454: }
455:
456: /** Delete specified line.
457: * Position of caret will not change.
458: * @param line number of line (beggining from 1)
459: */
460: public void deleteLine(int line) {
461: delete(getLineOffset(line), getText(line).length());
462: }
463:
464: /** Deletes characters between column1 and column2 (both are included)
465: * on the specified line.
466: * @param lineNumber number of line (beggining from 1)
467: * @param column1 column position where to start deleting (beggining from 1)
468: * @param column2 column position where to stop deleting (beggining from 1) */
469: public void delete(int lineNumber, int column1, int column2) {
470: delete(getLineOffset(lineNumber) + column1 - 1, column2
471: - column1 + 1);
472: }
473:
474: /********************** Caret manipulation ************************/
475:
476: /** Returns current line number.
477: * @return number of line where the caret stays (first line == 1)
478: */
479: public int getLineNumber() {
480: StyledDocument doc = (StyledDocument) txtEditorPane()
481: .getDocument();
482: int offset = txtEditorPane().getCaretPosition();
483: return NbDocument.findLineNumber(doc, offset) + 1;
484: }
485:
486: /** Pushes key of requested key code. */
487: public void pushKey(int keyCode) {
488: // need to request focus before any key push
489: this .requestFocus();
490: txtEditorPane().pushKey(keyCode);
491: }
492:
493: /** Pushes Home key (KeyEvent.VK_HOME) */
494: public void pushHomeKey() {
495: pushKey(KeyEvent.VK_HOME);
496: }
497:
498: /** Pushes End key (KeyEvent.VK_END) */
499: public void pushEndKey() {
500: pushKey(KeyEvent.VK_END);
501: }
502:
503: /** Pushes Tab key (KeyEvent.VK_TAB) */
504: public void pushTabKey() {
505: pushKey(KeyEvent.VK_TAB);
506: }
507:
508: /** Pushes Down key (KeyEvent.VK_DOWN) */
509: public void pushDownArrowKey() {
510: pushKey(KeyEvent.VK_DOWN);
511: }
512:
513: /** Pushes Up key (KeyEvent.VK_UP) */
514: public void pushUpArrowKey() {
515: pushKey(KeyEvent.VK_UP);
516: }
517:
518: /** Returns offset of the beginning of a line.
519: * @param lineNumber number of line (starts at 1)
520: * @return offset offset of line from the beginning of a file
521: */
522: private int getLineOffset(int lineNumber) {
523: try {
524: StyledDocument doc = (StyledDocument) txtEditorPane()
525: .getDocument();
526: return NbDocument.findLineOffset(doc, lineNumber - 1);
527: } catch (IndexOutOfBoundsException e) {
528: throw new JemmyException("Invalid line number "
529: + lineNumber, e);
530: }
531: }
532:
533: /** Sets caret position relatively to current position.
534: * @param relativeMove count of charaters to move caret
535: */
536: public void setCaretPositionRelative(int relativeMove) {
537: setCaretPosition(txtEditorPane().getCaretPosition()
538: + relativeMove);
539: }
540:
541: /** Sets caret position to the beginning of specified line.
542: * Lines are numbered from 1, so setCaretPosition(1) will set caret
543: * to the beginning of the first line.
544: * @param lineNumber number of line (beggining from 1)
545: */
546: public void setCaretPositionToLine(int lineNumber) {
547: txtEditorPane().setCaretPosition(getLineOffset(lineNumber));
548: }
549:
550: /** Sets caret position to the end of specified line.
551: * Lines are numbered from 1, so setCaretPosition(1) will set caret
552: * to the end of the first line.
553: * @param lineNumber number of line (beggining from 1)
554: */
555: public void setCaretPositionToEndOfLine(int lineNumber) {
556: // getText returns contents of line plus \n, that's why we use length()-1
557: txtEditorPane().setCaretPosition(
558: getLineOffset(lineNumber)
559: + getText(lineNumber).length() - 1);
560: }
561:
562: /** Sets caret position to specified line and column
563: * @param lineNumber line number where to set caret
564: * @param column column where to set caret (1 means beginning of the row)
565: */
566: public void setCaretPosition(int lineNumber, int column) {
567: setCaretPosition(getLineOffset(lineNumber) + column - 1);
568: }
569:
570: /** Sets caret to desired position.
571: * @param position a position to set caret to (number of characters from
572: * the beggining of the file - 0 means beginning of the file).
573: */
574: public void setCaretPosition(int position) {
575: if (position < 0 || position > getText().length()) {
576: throw new JemmyException("Invalid caret position "
577: + position);
578: }
579: txtEditorPane().setCaretPosition(position);
580: }
581:
582: /** Sets caret position before or after index-th occurence of given string.
583: * @param text text to be searched
584: * @param index index of text occurence (first occurence has index 0)
585: * @param before if true put caret before text, otherwise after.
586: */
587: public void setCaretPosition(String text, int index, boolean before) {
588: setCaretPosition(txtEditorPane().getPositionByText(text, index)
589: + (before ? 0 : text.length()));
590: }
591:
592: /** Sets caret position before or after first occurence of given string.
593: * @param text text to be searched
594: * @param before if true put caret before text, otherwise after.
595: */
596: public void setCaretPosition(String text, boolean before) {
597: setCaretPosition(text, 0, before);
598: }
599:
600: /**************************** Annotations ******************************/
601: /************** thanks to Jan Lahoda for valuable input ***************/
602:
603: /** Gets an array of annotations attached to given line.
604: * @param lineNumber number of line (beggining from 1)
605: * @return an array of org.openide.text.Annotation instances
606: * @see #getAnnotationShortDescription
607: * @see #getAnnotationType
608: */
609: public Object[] getAnnotations(int lineNumber) {
610: return getAnnotations(getLine(lineNumber)).toArray(
611: new Annotation[0]);
612: }
613:
614: /** Gets annotations from given Line object
615: * @param line instance of org.openide.text.Line
616: * @return list of annotations
617: */
618: private List getAnnotations(Object line) {
619: try {
620: Method getAnnotations = Annotatable.class
621: .getDeclaredMethod("getAnnotations", null);
622: getAnnotations.setAccessible(true);
623: return (List) getAnnotations.invoke(line, null);
624: } catch (Exception e) {
625: throw new JemmyException(
626: "getAnnotations() by reflection failed.", e);
627: }
628: }
629:
630: /**Gets all annotations for current editor (Document).
631: * @return array of org.openide.text.Annotation containing all annotations
632: * attached to this editor.
633: * @see #getAnnotationShortDescription
634: * @see #getAnnotationType
635: */
636: public Object[] getAnnotations() {
637: Document doc = txtEditorPane().getDocument();
638: DataObject dob = (DataObject) doc
639: .getProperty(Document.StreamDescriptionProperty);
640:
641: // get line annotations
642: Set set = ((LineCookie) dob.getCookie(LineCookie.class))
643: .getLineSet();
644: Iterator iter = set.getLines().iterator();
645: ArrayList result = new ArrayList();
646: while (iter.hasNext()) {
647: result.addAll(getAnnotations((Line) iter.next()));
648: }
649: // get error and override parser annotations
650: Class javaEditorClass = null;
651: try {
652: javaEditorClass = Class
653: .forName("org.netbeans.modules.java.JavaEditor");
654: } catch (ClassNotFoundException e) {
655: // print only warning. Class JavaEditor don't need to be present when
656: // java module is uninstalled.
657: getOutput()
658: .printLine(
659: "WARNING: Class org.netbeans.modules.java.JavaEditor not found.");
660: }
661: Object javaEditorInstance = dob.getCookie(javaEditorClass);
662: if (javaEditorInstance != null) {
663: ArrayList errorAnnotations;
664: try {
665: java.lang.reflect.Field annot = javaEditorClass
666: .getDeclaredField("errorAnnotations");
667: annot.setAccessible(true);
668: errorAnnotations = (ArrayList) annot
669: .get(javaEditorInstance);
670: } catch (Exception e) {
671: throw new JemmyException(
672: "Get errorAnnotations field failed.", e);
673: }
674: result.addAll(errorAnnotations);
675: ArrayList overrideAnnotations;
676: try {
677: java.lang.reflect.Field annot = javaEditorClass
678: .getDeclaredField("overrideAnnotations");
679: annot.setAccessible(true);
680: overrideAnnotations = (ArrayList) annot
681: .get(javaEditorInstance);
682: } catch (Exception e) {
683: throw new JemmyException(
684: "Get overrideAnnotations field failed.", e);
685: }
686: result.addAll(overrideAnnotations);
687: }
688: // return all line and parser annotations together
689: return result.toArray(new Annotation[result.size()]);
690: }
691:
692: /** Returns a string uniquely identifying annotation. For editor bookmark
693: * it is for example
694: * org.netbeans.modules.editor.NbEditorKit.BOOKMARK_ANNOTATION_TYPE.
695: * @param annotation instance of org.openide.text.Annotation
696: * @return a string uniquely identifying annotation
697: * @see #getAnnotations()
698: *@see #getAnnotations(int)
699: */
700: public static String getAnnotationType(Object annotation) {
701: return ((Annotation) annotation).getAnnotationType();
702: }
703:
704: /** Returns a short description of annotation. It is localized.
705: * @param annotation instance of org.openide.text.Annotation
706: * @return a short description of annotation according to current locale
707: */
708: public static String getAnnotationShortDescription(Object annotation) {
709: return ((Annotation) annotation).getShortDescription();
710: }
711:
712: /***************** Methods for toolbar manipulation *******************/
713:
714: /** Return JButtonOperator representing a toolbar button found by given
715: * tooltip within the Source Editor.
716: * @param buttonTooltip tooltip of toolbar button
717: * @return JButtonOperator instance of found toolbar button
718: */
719: public JButtonOperator getToolbarButton(String buttonTooltip) {
720: ToolbarButtonChooser chooser = new ToolbarButtonChooser(
721: buttonTooltip, getComparator());
722: return new JButtonOperator(JButtonOperator.waitJButton(
723: (Container) this .getSource(), chooser));
724: }
725:
726: /** Return JButtonOperator representing index-th toolbar button within
727: * the Source Editor.
728: * @param index index of toolbar button to find
729: * @return JButtonOperator instance of found toolbar button
730: */
731: public JButtonOperator getToolbarButton(int index) {
732: // finds JToolbar
733: ComponentChooser chooser = new ComponentChooser() {
734: public boolean checkComponent(Component comp) {
735: return comp instanceof JToolBar;
736: }
737:
738: public String getDescription() {
739: return "javax.swing.JToolBar";
740: }
741: };
742: Container toolbar = (Container) findComponent(
743: (Container) getSource(), chooser);
744: if (toolbar == null) {
745: throw new JemmyException("Toolbar not present.");
746: }
747: // if "quick browse" combo box is present, skip first button (MetalComboBoxButton usualy)
748: Component combo = JComboBoxOperator.findJComboBox(toolbar,
749: ComponentSearcher.getTrueChooser("JComboBox"));
750: if (combo != null) {
751: index++;
752: }
753: return new JButtonOperator(JButtonOperator.waitJButton(
754: (Container) toolbar, ComponentSearcher
755: .getTrueChooser("JButton"), index));
756: }
757:
758: /** Pushes popup menu on toolbar. It doesn't matter on which position it is
759: * invoked, everytime it is the same. That's why popup menu is invoked on
760: * the toolbar button with index 0. To switch toolbar on use Options ->
761: * Editing -> Editor Settings -> Toolbar Visible -> true.
762: * @param popupPath path to menu item (e.g. "Toolbar Visible")
763: */
764: public void pushToolbarPopupMenu(String popupPath) {
765: getToolbarButton(0).clickForPopup();
766: new JPopupMenuOperator().pushMenu(popupPath, "|");
767: }
768:
769: /** Chooser which can be used to find a component with given tooltip,
770: * in this case a toolbar button.
771: */
772: private static class ToolbarButtonChooser implements
773: ComponentChooser {
774: private String buttonTooltip;
775: private StringComparator comparator;
776:
777: public ToolbarButtonChooser(String buttonTooltip,
778: StringComparator comparator) {
779: this .buttonTooltip = buttonTooltip;
780: this .comparator = comparator;
781: }
782:
783: public boolean checkComponent(Component comp) {
784: return comparator.equals(((JComponent) comp)
785: .getToolTipText(), buttonTooltip);
786: }
787:
788: public String getDescription() {
789: return "Toolbar button with tooltip \"" + buttonTooltip
790: + "\".";
791: }
792: }
793:
794: /** Returns current modify state of edited source
795: * @return boolean true when edited source is modified
796: */
797: public boolean isModified() {
798: return getName().endsWith("*");
799: }
800:
801: /** Waits for given modified state of edited source.
802: * @param modified boolean true waits for file state change to modified, false for change to
803: * unmodified (saved).
804: * Throws TimeoutExpiredException when EditorOperator.WaitModifiedTimeout expires.
805: */
806: public void waitModified(final boolean modified) {
807: try {
808: Waiter waiter = new Waiter(new Waitable() {
809: public Object actionProduced(Object obj) {
810: return isModified() == modified ? new Object()
811: : null;
812: }
813:
814: public String getDescription() {
815: return ("Wait Modified State");
816: }
817: });
818: Timeouts times = getTimeouts().cloneThis();
819: times.setTimeout("Waiter.WaitingTime", times
820: .getTimeout("EditorOperator.WaitModifiedTimeout"));
821: waiter.setTimeouts(times);
822: waiter.setOutput(getOutput());
823: waiter.waitAction(null);
824: } catch (InterruptedException e) {
825: }
826: }
827:
828: /** Performs save action with optional verification. */
829: public void save() {
830: new SaveAction().perform(this );
831: if (getVerification())
832: waitModified(false);
833: }
834:
835: public void closeAndCancelByPopup() {
836: this .clickForPopup();
837: new JPopupMenuOperator().pushMenu("Close");
838: new EventTool().waitNoEvent(500);
839:
840: try {
841: JDialogOperator qDialog = new JDialogOperator("Question");
842: new QueueTool().waitEmpty(100);
843: new JButtonOperator(qDialog, "Cancel").push();
844: } catch (Exception e) {
845: }
846: }
847:
848: /** Performs verification by accessing all sub-components */
849: public void verify() {
850: txtEditorPane();
851: cboQuickBrowse();
852: lblInputMode();
853: lblRowColumn();
854: lblStatusBar();
855: }
856:
857: /** SubChooser to determine Editor TopComponent
858: * Used in findTopComponent method.
859: */
860: private static final class EditorSubchooser implements
861: ComponentChooser {
862: String ID;
863:
864: public EditorSubchooser(String ID) {
865: this .ID = ID;
866: }
867:
868: public boolean checkComponent(Component comp) {
869: return (comp instanceof CloneableEditor
870: && ((CloneableEditor) comp).getName().indexOf(ID) != -1 && comp
871: .isShowing());
872: }
873:
874: public String getDescription() {
875: return "org.openide.text.CloneableEditor";
876: }
877: }
878: }
|