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.spi.editor.completion.support;
043:
044: import javax.swing.SwingUtilities;
045: import javax.swing.text.Document;
046: import javax.swing.text.JTextComponent;
047: import org.netbeans.spi.editor.completion.CompletionResultSet;
048: import org.netbeans.spi.editor.completion.CompletionTask;
049: import org.openide.util.RequestProcessor;
050:
051: /**
052: * Asynchronous completion task allowing asynchronous query execution
053: * through {@link AsyncCompletionQuery}.
054: * <br>
055: * This is a final class and all the logic must be defined
056: * in an implementation of {@link AsyncCompletionQuery} that must
057: * be passed to constructor of this task.
058: *
059: * @see AsyncCompletionQuery
060: * @author Miloslav Metelka, Dusan Balek
061: * @version 1.00
062: */
063:
064: public final class AsyncCompletionTask implements CompletionTask,
065: Runnable {
066:
067: private final AsyncCompletionQuery query;
068:
069: private final JTextComponent component;
070:
071: private Document doc;
072:
073: private int queryCaretOffset;
074:
075: private CompletionResultSet queryResultSet;
076:
077: private CompletionResultSet refreshResultSet;
078:
079: private RequestProcessor.Task rpTask;
080:
081: /** Whether this task is cancelled. */
082: private boolean cancelled;
083:
084: /** Whether query was already invoked on this task. */
085: private boolean queryInvoked;
086:
087: /**
088: * Construct asynchronous task for the given component.
089: *
090: * @param query non-null query implementation.
091: * @param component text component to operate with. The completion infrastructure
092: * will cancel the task if the component or its associated document would change.
093: * <br>
094: * It may be null to indicate that no component was provided.
095: */
096: public AsyncCompletionTask(AsyncCompletionQuery query,
097: JTextComponent component) {
098: assert (query != null) : "Query must be non-null";
099: this .query = query;
100: this .component = component;
101: query.initTask(this );
102: }
103:
104: /**
105: * Constructor for the case when there is no valid component
106: * which can happen when creating task for documentation or tooltip computation.
107: *
108: * @param query non-null query implementation.
109: */
110: public AsyncCompletionTask(AsyncCompletionQuery query) {
111: this (query, null);
112: }
113:
114: /**
115: * Called by completion infrastructure in AWT thread to populate
116: * the given result set with data.
117: */
118: public void query(CompletionResultSet resultSet) {
119: assert (resultSet != null);
120: assert (SwingUtilities.isEventDispatchThread());
121: if (component != null) {
122: doc = component.getDocument();
123: } else {
124: doc = null;
125: }
126: queryInvoked = true;
127:
128: synchronized (this ) {
129: performQuery(resultSet);
130: }
131: }
132:
133: /**
134: * Called by completion infrastructure in AWT thread once there
135: * is a change in the component (caret position changes or document
136: * gets modified).
137: * <br>
138: * The results should be fired into the newly provided completion listener.
139: */
140: public void refresh(CompletionResultSet resultSet) {
141: assert (SwingUtilities.isEventDispatchThread());
142: assert !cancelled : "refresh() called on canceled task"; // NOI18N
143: if (queryInvoked) {
144: assert (resultSet != null);
145: refreshResultSet = resultSet;
146: refreshImpl();
147: } else {
148: query.preQueryUpdate(component);
149: }
150: }
151:
152: /**
153: * Called by completion infrastructure to cancel the running task.
154: */
155: public void cancel() {
156: cancelled = true;
157: synchronized (this ) {
158: if (rpTask != null) {
159: rpTask.cancel();
160: rpTask = null;
161: }
162: }
163: }
164:
165: private void performQuery(CompletionResultSet resultSet) {
166: // Runs in AWT thread only
167: if (component != null) {
168: queryCaretOffset = component.getSelectionStart();
169: } else {
170: queryCaretOffset = -1;
171: }
172:
173: query.prepareQuery(component);
174: synchronized (this ) {
175: queryResultSet = resultSet;
176: rpTask = RequestProcessor.getDefault().post(this );
177: }
178: }
179:
180: void refreshImpl() {
181: // Always called in AWT thread only
182: CompletionResultSet resultSet;
183: boolean rpTaskFinished;
184: synchronized (this ) {
185: rpTaskFinished = (rpTask == null);
186: resultSet = refreshResultSet; // refreshResultSet checked in run()
187: }
188: if (resultSet != null) {
189: if (rpTaskFinished) { // query finished already
190: synchronized (this ) {
191: refreshResultSet = null;
192: }
193: // Synchronously call the refresh()
194: if (query.canFilter(component)) {
195: query.filter(resultSet);
196: assert resultSet.isFinished() : toString()
197: + ": query.filter(): Result set not finished by resultSet.finish()"; // NOI18N
198: } else { // cannot filter and query stopped => another full query
199: performQuery(resultSet);
200: }
201:
202: } else { // pending query not finished yet
203: if (!query.canFilter(component)) { // query should attempted to be stopped by canFilter()
204: // Leave the ongoing query to be finished and once that happens
205: // ask for another canFilter()
206: } // Let the query finish and schedule refreshing by (refreshResultSet != null)
207: }
208: }
209: }
210:
211: /**
212: * This method will be run() from the RequestProcessor during
213: * performing of the query.
214: */
215: public void run() {
216: // First check whether there was not request yet to stop the query: (queryResultSet == null)
217: CompletionResultSet resultSet = queryResultSet;
218: if (resultSet != null) {
219: // Perform the querying (outside of synchronized section)
220: query.query(resultSet, doc, queryCaretOffset);
221: assert resultSet.isFinished() : toString()
222: + ": query.query(): Result set not finished by resultSet.finish()"; // NOI18N
223: }
224:
225: synchronized (this ) {
226: rpTask = null;
227: queryResultSet = null;
228: // Check for pending refresh
229: if (refreshResultSet != null) {
230: // Post refresh computation into AWT thread
231: SwingUtilities.invokeLater(new Runnable() {
232: public void run() {
233: refreshImpl();
234: }
235: });
236: }
237: }
238: }
239:
240: synchronized boolean isCancelled() {
241: return cancelled;
242: }
243:
244: public String toString() {
245: return "AsyncCompletionTask: query=" + query; // NOI18N
246: }
247: }
|