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: package org.netbeans.test.java.editor.completion;
042:
043: import java.io.*;
044: import java.util.Arrays;
045: import java.util.Collection;
046: import java.util.Comparator;
047: import java.util.Iterator;
048: import java.util.List;
049: import javax.swing.JEditorPane;
050: import javax.swing.SwingUtilities;
051: import javax.swing.text.BadLocationException;
052: import lib.EditorTestCase;
053: import lib.EditorTestCase.ValueResolver;
054: import org.netbeans.api.editor.mimelookup.MimeLookup;
055: import org.netbeans.api.editor.mimelookup.MimePath;
056: import org.openide.filesystems.FileObject;
057: import org.netbeans.editor.*;
058: import org.netbeans.jellytools.EditorOperator;
059: import org.netbeans.junit.ide.ProjectSupport;
060: import org.netbeans.modules.editor.completion.CompletionImpl;
061: import org.netbeans.modules.editor.completion.CompletionResultSetImpl;
062: import org.netbeans.modules.editor.java.JavaCompletionItem;
063: import org.netbeans.modules.editor.java.JavaCompletionProvider;
064: import org.netbeans.modules.editor.java.LazyTypeCompletionItem;
065: import org.netbeans.spi.editor.completion.CompletionItem;
066: import org.netbeans.spi.editor.completion.CompletionProvider;
067: import org.netbeans.spi.editor.completion.CompletionTask;
068: import org.netbeans.spi.editor.completion.LazyCompletionItem;
069: import org.openide.cookies.CloseCookie;
070: import org.openide.cookies.EditorCookie;
071: import org.openide.cookies.SaveCookie;
072: import org.openide.filesystems.FileUtil;
073: import org.openide.loaders.DataObject;
074: import org.openide.util.Lookup;
075:
076: /**<FONT COLOR="#CC3333" FACE="Courier New, Monospaced" SIZE="+1">
077: * <B>
078: * Editor module API test: completion/CompletionTest
079: * </B>
080: * </FONT>
081: *
082: * <P>
083: * <B>What it tests:</B><BR>
084: * The purpose of this test is to test Java (and HTML) code completion. This test
085: * is done on some layer between user and API. It uses file and completion
086: * is called on the top of the file, but it is never shown.
087: * </P>
088: *
089: * <P>
090: * <B>How it works:</B><BR>
091: * TestFile is opened, given text is written to it, and code completion is
092: * asked to return response. The type of completion is defined by the type of
093: * the file. Unfortunately, it is not possible to ask completion for response
094: * without opening the file.
095: * </P>
096: *
097: * <P>
098: * <B>Settings:</B><BR>
099: * This test is not complete test, it's only stub, so for concrete test instance
100: * it's necessary to provide text to add and whether the response should be
101: * sorted. No more settings needed, when runned on clean build.
102: * </P>
103: *
104: * <P>
105: * <B>Output:</B><BR>
106: * The output should be completion reponse in human readable form.
107: * </P>
108: *
109: * <P>
110: * <B>Possible reasons of failure:</B><BR>
111: * <UL>
112: * <LI>An exception when obtaining indent engine (for example if it doesn't exist).</LI>
113: * <LI>An exception when writting to indent engine.</LI>
114: * <LI>Possibly unrecognized MIME type.</LI>
115: * <LI>Indent engine error.</LI>
116: * <LI>The file can not be opened. This test must be able to open the file.
117: * The test will fail if it is not able to open the file. In case it starts
118: * opening sequence, but the editor is not opened, it may lock.</LI>
119: * </UL>
120: * </P>
121: *
122: * @author Jan Lahoda
123: * @version 1.0
124: */
125: public class CompletionTest extends java.lang.Object {
126:
127: private static final long OPENING_TIMEOUT = 60 * 1000;
128: private static final long SLEEP_TIME = 1000;
129:
130: /** Creates new CompletionTest */
131: public CompletionTest() {
132: }
133:
134: private class ResultReadyResolver implements ValueResolver {
135: private CompletionResultSetImpl crs;
136:
137: public ResultReadyResolver(CompletionResultSetImpl crs) {
138: this .crs = crs;
139: }
140:
141: public Object getValue() {
142: return crs.isFinished();
143: }
144: }
145:
146: private void completionQuery(PrintWriter out, PrintWriter log,
147: JEditorPane editor, boolean unsorted, int queryType) {
148: BaseDocument doc = Utilities.getDocument(editor);
149: SyntaxSupport support = doc.getSyntaxSupport();
150:
151: CompletionImpl completion = CompletionImpl.get();
152: String mimeType = (String) doc.getProperty("mimeType");
153: Lookup lookup = MimeLookup.getLookup(MimePath.get(mimeType));
154: Collection<? extends CompletionProvider> col = lookup
155: .lookupAll(CompletionProvider.class);
156: JavaCompletionProvider jcp = null;
157: for (Iterator<? extends CompletionProvider> it = col.iterator(); it
158: .hasNext();) {
159: CompletionProvider completionProvider = it.next();
160: if (completionProvider instanceof JavaCompletionProvider)
161: jcp = (JavaCompletionProvider) completionProvider;
162: }
163: if (jcp == null)
164: log.println("JavaCompletionProvider not found");
165: CompletionTask task = jcp.createTask(queryType, editor);
166: CompletionResultSetImpl result = completion
167: .createTestResultSet(task, queryType);
168: task.query(result.getResultSet());
169: EditorTestCase.waitMaxMilisForValue(5000,
170: new ResultReadyResolver(result), Boolean.TRUE);
171: if (!result.isFinished())
172: log.println("Result is not finished");
173: List<? extends CompletionItem> list = result.getItems();
174: CompletionItem[] array = list.toArray(new CompletionItem[] {});
175: if (!unsorted) {
176: Arrays.sort(array, new Comparator<CompletionItem>() {
177: public int compare(CompletionItem arg0,
178: CompletionItem arg1) {
179: Integer p1 = arg0.getSortPriority();
180: Integer p2 = arg1.getSortPriority();
181: if (p1.intValue() != p2.intValue())
182: return p1.compareTo(p2);
183: String a0 = getStringFromCharSequence(arg0
184: .getSortText());
185: String a1 = getStringFromCharSequence(arg1
186: .getSortText());
187: return a0.compareTo(a1);
188: }
189: });
190: }
191: for (int i = 0; i < array.length; i++) {
192: CompletionItem completionItem = array[i];
193: out.println(getStringFromCharSequence(completionItem
194: .getSortText()));
195: }
196: /*Completion completion = ExtUtilities.getCompletion(editor);
197: if (completion != null) {
198: CompletionQuery completionQuery = completion.getQuery();
199: if (completionQuery != null) {
200: CompletionQuery.Result query = completionQuery.query(editor, editor.getCaret().getDot(), support);
201:
202: if (query != null) {
203: List list = query.getData();
204:
205: if (list != null) {
206:
207: String[] texts = new String[list.size()];
208: for (int cntr = 0; cntr < list.size(); cntr++) {
209: texts[cntr] = list.get(cntr).toString();
210: };
211: if (sort)
212: Arrays.sort(texts);
213:
214: for (int cntr = 0; cntr < texts.length; cntr++) {
215: out.println(texts[cntr].toString());
216: };
217: } else {
218: log.println("CompletionTest: query.getData() == null");
219: throw new IllegalStateException("CompletionTest: query.getData() == null");
220: }
221: } else {
222: log.println("CompletionTest: completionQuery.query(pane, end, support) == null");
223: throw new IllegalStateException("CompletionTest: completionQuery.query(pane, end, support) == null");
224: }
225: } else {
226: log.println("CompletionTest: completion.getQuery() == null");
227: throw new IllegalStateException("CompletionTest: completion.getQuery() == null");
228: }
229: } else {
230: log.println("CompletionTest: ExtUtilities.getCompletion(pane) == null");
231: throw new IllegalStateException("CompletionTest: ExtUtilities.getCompletion(pane) == null");
232: }*/
233: }
234:
235: private String getStringFromCharSequence(CharSequence chs) {
236: int length = chs.length();
237: String text = chs.subSequence(0, length).toString();
238: return text;
239: }
240:
241: /**Currently, this method is supposed to be runned inside the AWT thread.
242: * If this condition is not fullfilled, an IllegalStateException is
243: * thrown. Do NOT modify this behaviour, or deadlock (or even Swing
244: * or NetBeans winsys data corruption) may occur.
245: *
246: * Currently threading model of this method is compatible with
247: * editor code completion threading model. Revise if this changes
248: * in future.
249: */
250: private void testPerform(PrintWriter out, PrintWriter log,
251: JEditorPane editor, boolean unsorted, String assign,
252: int lineIndex, int queryType) throws BadLocationException,
253: IOException {
254: if (!SwingUtilities.isEventDispatchThread())
255: throw new IllegalStateException(
256: "The testPerform method may be called only inside AWT event dispatch thread.");
257:
258: BaseDocument doc = Utilities.getDocument(editor);
259: int lineOffset = Utilities.getRowStartFromLineOffset(doc,
260: lineIndex - 1);
261:
262: editor.grabFocus();
263: editor.getCaret().setDot(lineOffset);
264: doc.insertString(lineOffset, assign, null);
265: reparseDocument((DataObject) doc
266: .getProperty(doc.StreamDescriptionProperty));
267: completionQuery(out, log, editor, unsorted, queryType);
268: }
269:
270: public void test(final PrintWriter out, final PrintWriter log,
271: final String assign, final boolean unsorted,
272: final File dataDir, final String projectName,
273: final String testFileName, final int line) throws Exception {
274: test(out, log, assign, unsorted, dataDir, projectName,
275: testFileName, line,
276: CompletionProvider.COMPLETION_QUERY_TYPE);
277: }
278:
279: public void test(final PrintWriter out, final PrintWriter log,
280: final String assign, final boolean unsorted,
281: final File dataDir, final String projectName,
282: final String testFileName, final int line,
283: final int queryType) throws Exception {
284: try {
285: log.println("Completion test start.");
286: log.flush();
287:
288: FileObject testFileObject = getTestFile(dataDir,
289: projectName, testFileName, log);
290: final DataObject testFile = DataObject.find(testFileObject);
291:
292: try {
293: Runnable test = new Runnable() {
294: public void run() {
295: try {
296: JEditorPane editor = getAnEditorPane(
297: testFile, log);
298: testPerform(out, log, editor, unsorted,
299: assign, line, queryType);
300: } catch (Exception e) {
301: e.printStackTrace(log);
302: }
303: }
304: };
305: if (SwingUtilities.isEventDispatchThread()) {
306: test.run();
307: } else {
308: SwingUtilities.invokeAndWait(test);
309: }
310: } finally {
311: testFile.setModified(false);
312: String fileName = testFileName.substring(testFileName
313: .lastIndexOf('/') + 1);
314: new EditorOperator(fileName).close();
315: //((CloseCookie) testFile.getCookie(CloseCookie.class)).close();
316: }
317: } catch (Exception e) {
318: e.printStackTrace(log);
319: throw e;
320: }
321: }
322:
323: private FileObject getTestFile(File dataDir, String projectName,
324: String testFile, PrintWriter log) throws IOException,
325: InterruptedException {
326: File projectFile = new File(dataDir, projectName);
327: FileObject project = FileUtil.toFileObject(projectFile);
328: Object prj = ProjectSupport.openProject(projectFile);
329:
330: if (prj == null)
331: throw new IllegalStateException("Given directory \""
332: + project + "\" does not contain a project.");
333:
334: log.println("Project found: " + prj);
335:
336: FileObject test = project.getFileObject("src/" + testFile);
337:
338: if (test == null)
339: throw new IllegalStateException(
340: "Given test file does not exist.");
341:
342: return test;
343: }
344:
345: // public static void main(String[] args) throws Exception {
346: // PrintWriter out = new PrintWriter(System.out);
347: // PrintWriter log = new PrintWriter(System.err);
348: // new CompletionTest().test(out, log, "int[] a; a.", false, "org/netbeans/test/editor/completion/data/testfiles/CompletionTestPerformer/TestFile.java", 20);
349: // out.flush();
350: // log.flush();
351: // }
352:
353: //Utility methods:
354: // private static void checkEditorCookie(DataObject od) {
355: // EditorCookie ec =(EditorCookie) od.getCookie(EditorCookie.class);
356: //
357: // if (ec == null)
358: // throw new IllegalStateException("Given file (\"" + od.getName() + "\") does not have EditorCookie.");
359: // }
360:
361: private static JEditorPane getAnEditorPane(DataObject file,
362: PrintWriter log) throws Exception {
363: EditorCookie cookie = (EditorCookie) file
364: .getCookie(EditorCookie.class);
365:
366: if (cookie == null)
367: throw new IllegalStateException("Given file (\""
368: + file.getName()
369: + "\") does not have EditorCookie.");
370:
371: JEditorPane[] panes = cookie.getOpenedPanes();
372: long start = System.currentTimeMillis();
373:
374: if (panes == null) {
375: //Prepare by opening a document. The actual opening into the editor
376: //should be faster (hopefully...).
377: cookie.openDocument();
378: cookie.open();
379: while ((panes = cookie.getOpenedPanes()) == null
380: && (System.currentTimeMillis() - start) < OPENING_TIMEOUT) {
381: try {
382: Thread.sleep(SLEEP_TIME);
383: } catch (InterruptedException e) {
384: e.printStackTrace(log);
385: }
386: }
387: ;
388:
389: log.println("Waiting spent: "
390: + (System.currentTimeMillis() - start) + "ms.");
391: }
392: ;
393:
394: if (panes == null)
395: throw new IllegalStateException(
396: "The editor was not opened. The timeout was: "
397: + OPENING_TIMEOUT + "ms.");
398:
399: return panes[0];
400: }
401:
402: // private static boolean isSomePaneOpened(DataObject file) {
403: // EditorCookie cookie = (EditorCookie) file.getCookie(EditorCookie.class);
404: //
405: // if (cookie == null) {
406: // return false;
407: // }
408: //
409: // return cookie.getOpenedPanes() != null;
410: // }
411:
412: private static void reparseDocument(DataObject file)
413: throws IOException {
414: saveDocument(file);
415: /* SourceCookie sc = (SourceCookie) file.getCookie(SourceCookie.class);
416:
417: if (sc != null) {
418: SourceElement se = sc.getSource();
419: se.prepare().waitFinished();
420: } else {
421: System.err.println("reparseDocument: SourceCookie cookie not found in testFile!");
422: }*/
423: // XXX waiting to reparse - should be repalaced by smtg more sofisticated
424: try {
425: Thread.sleep(1000);
426: } catch (InterruptedException e) {
427:
428: }
429: }
430:
431: private static void saveDocument(DataObject file)
432: throws IOException { //!!!WARNING: if this exception is thrown, the test may be locked (the file in editor may be modified, but not saved. problems with IDE finishing are supposed in this case).
433: SaveCookie sc = (SaveCookie) file.getCookie(SaveCookie.class);
434:
435: if (sc != null) {
436: sc.save();
437: }
438: }
439:
440: }
|