KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > search > Manager


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 2004-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.search;
21
22 import java.awt.EventQueue JavaDoc;
23 import java.lang.ref.Reference JavaDoc;
24 import java.lang.reflect.InvocationTargetException JavaDoc;
25 import java.lang.reflect.Method JavaDoc;
26 import org.openide.DialogDisplayer;
27 import org.openide.ErrorManager;
28 import org.openide.NotifyDescriptor;
29 import org.openide.awt.StatusDisplayer;
30 import org.openide.util.NbBundle;
31 import org.openide.util.RequestProcessor;
32 import org.openide.util.Task;
33 import org.openide.util.TaskListener;
34 import org.openidex.search.SearchGroup;
35 import static org.netbeans.modules.search.ReplaceTask.ResultStatus.SUCCESS;
36 import static org.netbeans.modules.search.ReplaceTask.ResultStatus.PRE_CHECK_FAILED;
37 import static org.netbeans.modules.search.ReplaceTask.ResultStatus.PROBLEMS_ENCOUNTERED;
38
39 /**
40  * Manager of the Search module's activities.
41  * It knows which tasks are running and manages the module's actions so that
42  * no two conflicting tasks are running at a moment.
43  *
44  * @see <a HREF="doc-files/manager-state-diagram.png">State diagram</a>
45  * @author Marian Petras
46  */

47 final class Manager {
48     
49     /**
50      * timeout for cleanup in the case that the module is being uninstalled
51      * (in milliseconds)
52      */

53     private static final int CLEANUP_TIMEOUT_MILLIS = 3000;
54     
55
56     static final int NO_TASK = 0;
57     
58     static final int SEARCHING = 0x01;
59     
60     static final int CLEANING_RESULT = 0x02;
61
62     static final int PRINTING_DETAILS = 0x04;
63     
64     static final int REPLACING = 0x08;
65     
66     static final int EVENT_SEARCH_STARTED = 1;
67     
68     static final int EVENT_SEARCH_FINISHED = 2;
69     
70     static final int EVENT_SEARCH_INTERRUPTED = 3;
71     
72     static final int EVENT_SEARCH_CANCELLED = 4;
73     
74     private static final Manager instance = new Manager();
75     
76     
77     private boolean moduleBeingUninstalled = false;
78
79
80     private final Object JavaDoc lock = new Object JavaDoc();
81
82     private int state = NO_TASK;
83
84     private int pendingTasks = 0;
85     
86     private TaskListener taskListener;
87     
88     private SearchTask currentSearchTask;
89     
90     private SearchTask pendingSearchTask;
91
92     private SearchTask lastSearchTask;
93     
94     private ReplaceTask currentReplaceTask;
95     
96     private ReplaceTask pendingReplaceTask;
97
98     private PrintDetailsTask currentPrintDetailsTask;
99     
100     private PrintDetailsTask pendingPrintDetailsTask;
101
102     private Task searchTask;
103     
104     private Task replaceTask;
105
106     private Task cleanResultTask;
107     
108     private Task printDetailsTask;
109
110     private ResultModel resultModelToClean;
111
112     private boolean searchWindowOpen = false;
113     
114     private Reference JavaDoc outputWriterRef;
115     
116
117     /**
118      */

119     static Manager getInstance() {
120         return instance;
121     }
122
123     /**
124      */

125     private Manager() { }
126     
127     /*
128      * INVARIANTS:
129      * #1: If the Search Results window is open, its root node displays:
130      * - if the search task is in progress:
131      * - summary of results
132      * - if the Search module is inactive:
133      * - summary of current results (continuously updated)
134      * - if the search task in scheduled but another task is blocking it:
135      * - name of the current task blocking the search
136      * #2: At most one result model exists at a single moment.
137      */

138
139     /**
140      */

141     void scheduleSearchTask(SearchTask task) {
142         assert EventQueue.isDispatchThread();
143         
144         synchronized (lock) {
145             ResultView.getInstance().setResultModel(null);
146             if (currentSearchTask != null) {
147                 currentSearchTask.stop(false);
148             }
149             if (resultModelToClean != null) {
150                 pendingTasks |= CLEANING_RESULT;
151             }
152             pendingTasks |= SEARCHING;
153             pendingSearchTask = task;
154             lastSearchTask = task;
155             if (state == NO_TASK) {
156                 processNextPendingTask();
157             } else {
158                 notifySearchPending(state); //invariant #1
159
}
160         }
161     }
162     
163     /**
164      */

165     void scheduleReplaceTask(ReplaceTask task) {
166         assert EventQueue.isDispatchThread();
167         
168         synchronized (lock) {
169             assert (state == NO_TASK) && (pendingTasks == 0);
170             
171             pendingTasks |= REPLACING;
172             pendingReplaceTask = task;
173             processNextPendingTask();
174         }
175     }
176     
177     /**
178      */

179     void scheduleSearchTaskRerun() {
180         assert EventQueue.isDispatchThread();
181         
182         synchronized (lock) {
183             SearchTask newSearchTask = lastSearchTask.createNewGeneration();
184             lastSearchTask = null;
185             scheduleSearchTask(newSearchTask);
186         }
187     }
188     
189     /**
190      */

191     void schedulePrintingDetails(Object JavaDoc[] matchingObjects,
192                                  SearchGroup searchGroup) {
193         synchronized (lock) {
194             assert state == NO_TASK;
195             pendingTasks |= PRINTING_DETAILS;
196             
197             pendingPrintDetailsTask = new PrintDetailsTask(
198                     matchingObjects,
199                     searchGroup);
200             processNextPendingTask();
201         }
202     }
203     
204     /**
205      * Queries whether the user should be allowed to initiate a new search.
206      * For example, the user should not be allowed to do so if the last
207      * replace action has not finished yet.
208      *
209      * @return message to the user, describing the reason why a new search
210      * cannot be started, or {@code null} if there is no such reason
211      * (i.e. if a new search may be started)
212      */

213     String JavaDoc mayStartSearching() {
214         boolean replacing;
215         
216         synchronized (lock) {
217             replacing = (state == REPLACING);
218         }
219         
220         String JavaDoc msgKey = replacing ? "MSG_Cannot_start_search__replacing"//NOI18N
221
: null;
222         return (msgKey != null) ? NbBundle.getMessage(getClass(), msgKey)
223                                 : null;
224     }
225     
226     /**
227      */

228     private void notifySearchStarted() {
229         notifySearchTaskStateChange(EVENT_SEARCH_STARTED);
230     }
231     
232     /**
233      */

234     private void notifySearchFinished() {
235         notifySearchTaskStateChange(EVENT_SEARCH_FINISHED);
236     }
237     
238     /**
239      */

240     private void notifySearchInterrupted() {
241         notifySearchTaskStateChange(EVENT_SEARCH_INTERRUPTED);
242     }
243     
244     /**
245      */

246     private void notifySearchCancelled() {
247         notifySearchTaskStateChange(EVENT_SEARCH_CANCELLED);
248     }
249     
250     /**
251      * Notifies the result window of a search task's state change.
252      *
253      * @param changeType constant describing what happened
254      * - one of the EVENT_xxx constants
255      */

256     private void notifySearchTaskStateChange(final int changeType) {
257         synchronized (lock) {
258             if (!searchWindowOpen) {
259                 return;
260             }
261         }
262         callOnWindowFromAWT("searchTaskStateChanged", //NOI18N
263
new Integer JavaDoc(changeType));
264     }
265
266     /**
267      */

268     private void notifySearchPending(final int blockingTask) {
269         if (!searchWindowOpen) {
270             return;
271         }
272         callOnWindowFromAWT("notifySearchPending", //NOI18N
273
new Integer JavaDoc(blockingTask));
274     }
275     
276     /**
277      */

278     private void notifyReplaceFinished() {
279         assert Thread.holdsLock(lock);
280         assert currentReplaceTask != null;
281         
282         ReplaceTask.ResultStatus resultStatus
283                 = currentReplaceTask.getResultStatus();
284         if (resultStatus == SUCCESS) {
285             StatusDisplayer.getDefault().setStatusText(
286                     NbBundle.getMessage(getClass(), "MSG_Success")); //NOI18N
287
if (searchWindowOpen) {
288                 callOnWindowFromAWT("closeAndSendFocusToEditor", false);//NOI18N
289
}
290         } else {
291             String JavaDoc msgKey = (resultStatus == PRE_CHECK_FAILED)
292                             ? "MSG_Issues_found_during_precheck" //NOI18N
293
: "MSG_Issues_found_during_replace"; //NOI18N
294
String JavaDoc title = NbBundle.getMessage(getClass(), msgKey);
295             displayIssuesFromAWT(title,
296                                  currentReplaceTask.getProblems(),
297                                  resultStatus != PRE_CHECK_FAILED);
298             if (resultStatus == PRE_CHECK_FAILED) {
299                 offerRescanAfterIssuesFound();
300             }
301         }
302     }
303     
304     /**
305      */

306     private void offerRescanAfterIssuesFound() {
307         assert Thread.holdsLock(lock);
308         assert currentReplaceTask != null;
309         
310         String JavaDoc msg = NbBundle.getMessage(getClass(),
311                                          "MSG_IssuesFound_Rescan_"); //NOI18N
312
NotifyDescriptor nd = new NotifyDescriptor.Message(
313                                             msg,
314                                             NotifyDescriptor.QUESTION_MESSAGE);
315         String JavaDoc rerunOption = NbBundle.getMessage(getClass(),
316                                                  "LBL_Rerun"); //NOI18N
317
nd.setOptions(new Object JavaDoc[] {rerunOption,
318                                     NotifyDescriptor.CANCEL_OPTION});
319         Object JavaDoc dlgResult = DialogDisplayer.getDefault().notify(nd);
320         if (rerunOption.equals(dlgResult)) {
321             /*
322              * The rescan method calls 'scheduleSearchTaskRerun()' on this.
323              * But it will wait until 'taskFinished()' returns, which is
324              * exactly what we need to keep consistency of the manager's fields
325              * like 'currentReplaceTask', 'replaceTask' and 'state'.
326              * Using this mechanism also requires that, when sending a method
327              * to the EventQueue thread, we use invokeLater(...) and not
328              * invokeAndWait(...).
329              */

330             callOnWindowFromAWT("rescan", false); //NOI18N
331
}
332     }
333     
334     /**
335      */

336     private void notifyPrintingDetailsFinished() {
337         if (!searchWindowOpen) {
338             return;
339         }
340         callOnWindowFromAWT("showAllDetailsFinished"); //NOI18N
341
}
342
343     /**
344      */

345     private void activateResultWindow() {
346         Method JavaDoc theMethod;
347         try {
348             theMethod = ResultView.class
349                         .getMethod("requestActive", new Class JavaDoc[0]); //NOI18N
350
} catch (NoSuchMethodException JavaDoc ex) {
351             throw new IllegalArgumentException JavaDoc();
352         }
353         callOnWindowFromAWT(theMethod, null);
354     }
355     
356     /**
357      */

358     private void displayIssuesFromAWT(String JavaDoc title,
359                                       String JavaDoc[] issues,
360                                       boolean att) {
361         Method JavaDoc theMethod;
362         try {
363             theMethod = ResultView.class.getDeclaredMethod(
364                                                 "displayIssuesToUser", //NOI18N
365
String JavaDoc.class,
366                                                 String JavaDoc[].class,
367                                                 Boolean.TYPE);
368         } catch (NoSuchMethodException JavaDoc ex) {
369             throw new IllegalStateException JavaDoc(ex);
370         }
371         callOnWindowFromAWT(theMethod,
372                             new Object JavaDoc[] {title, issues, Boolean.valueOf(att)},
373                             false);
374     }
375     
376     /**
377      * Calls a given method on the Search Results window, from the AWT thread.
378      *
379      * @param methodName name of the method to be called
380      */

381     private void callOnWindowFromAWT(final String JavaDoc methodName) {
382         callOnWindowFromAWT(methodName, true);
383     }
384     
385     /**
386      */

387     private void callOnWindowFromAWT(final String JavaDoc methodName,
388                                      final boolean wait) {
389         Method JavaDoc theMethod;
390         try {
391             theMethod = ResultView.class
392                         .getDeclaredMethod(methodName, new Class JavaDoc[0]);
393         } catch (NoSuchMethodException JavaDoc ex) {
394             throw new IllegalArgumentException JavaDoc();
395         }
396         callOnWindowFromAWT(theMethod, null, wait);
397     }
398     
399     /**
400      * Calls a given method on the Search Results window, from the AWT thread.
401      *
402      * @param methodName name of the method to be called
403      * @param param parameter to be passed to the method
404      */

405     private void callOnWindowFromAWT(final String JavaDoc methodName,
406                                      final Object JavaDoc param) {
407         callOnWindowFromAWT(methodName, param, true);
408     }
409     
410     /**
411      */

412     private void callOnWindowFromAWT(final String JavaDoc methodName,
413                                      final Object JavaDoc param,
414                                      final boolean wait) {
415         Method JavaDoc theMethod = null;
416         Method JavaDoc[] methods = ResultView.class.getDeclaredMethods();
417         for (int i = 0; i < methods.length; i++) {
418             Method JavaDoc method = methods[i];
419             if (method.getName().equals(methodName)) {
420                 Class JavaDoc[] parameterTypes = method.getParameterTypes();
421                 if (parameterTypes.length == 1) {
422                     Class JavaDoc paramType = parameterTypes[0];
423                     if ((param == null
424                                && !paramType.isPrimitive())
425                             || (paramType == Integer.TYPE)
426                                && (param instanceof Integer JavaDoc)
427                             || parameterTypes[0].isInstance(param)) {
428                         theMethod = method;
429                     }
430                 }
431             }
432         }
433         if (theMethod == null) {
434             throw new IllegalArgumentException JavaDoc();
435         }
436         callOnWindowFromAWT(theMethod, new Object JavaDoc[] {param}, wait);
437     }
438     
439     /**
440      */

441     private void callOnWindowFromAWT(final Method JavaDoc method,
442                                      final Object JavaDoc[] params) {
443         callOnWindowFromAWT(method, params, true);
444     }
445     
446     /**
447      */

448     private void callOnWindowFromAWT(final Method JavaDoc method,
449                                      final Object JavaDoc[] params,
450                                      final boolean wait) {
451         Runnable JavaDoc runnable = new Runnable JavaDoc() {
452             public void run() {
453                 final ResultView resultViewInstance = ResultView.getInstance();
454                 try {
455                     method.invoke(resultViewInstance, params);
456                 } catch (Exception JavaDoc ex) {
457                     ErrorManager.getDefault().notify(ex);
458                 }
459             }
460         };
461         if (EventQueue.isDispatchThread()) {
462             runnable.run();
463         } else {
464             if (wait) {
465                 try {
466                     EventQueue.invokeAndWait(runnable);
467                 } catch (InvocationTargetException JavaDoc ex1) {
468                     ErrorManager.getDefault().notify(ex1);
469                 } catch (Exception JavaDoc ex2) {
470                     ErrorManager.getDefault().notify(ErrorManager.ERROR, ex2);
471                 }
472             } else {
473                 EventQueue.invokeLater(runnable);
474             }
475         }
476     }
477     
478     /**
479      */

480     void searchWindowOpened() {
481         synchronized (lock) {
482             searchWindowOpen = true;
483         }
484     }
485
486     /**
487      */

488     void searchWindowClosed() {
489         assert EventQueue.isDispatchThread();
490         
491         synchronized (lock) {
492             searchWindowOpen = false;
493             
494             if (moduleBeingUninstalled) {
495                 return;
496             }
497             
498             if (currentSearchTask != null) {
499                 currentSearchTask.stop(false);
500             }
501             if (resultModelToClean != null) {
502                 pendingTasks |= CLEANING_RESULT;
503             }
504             pendingTasks &= ~SEARCHING;
505             pendingSearchTask = null;
506             lastSearchTask = null;
507             if (state == NO_TASK) {
508                 processNextPendingTask();
509             }
510         }
511     }
512     
513     /**
514      */

515     private void processNextPendingTask() {
516         synchronized (lock) {
517             assert state == NO_TASK;
518             if (resultModelToClean == null) {
519                 pendingTasks &= ~CLEANING_RESULT;
520             }
521             if ((pendingTasks & PRINTING_DETAILS) != 0) {
522                 if ((pendingTasks & SEARCHING) != 0) {
523                     notifySearchPending(PRINTING_DETAILS); //invariant #1
524
}
525                 startPrintingDetails();
526             } else if ((pendingTasks & CLEANING_RESULT) != 0) {
527                 if ((pendingTasks & SEARCHING) != 0) {
528                     notifySearchPending(CLEANING_RESULT); //invariant #1
529
}
530                 startCleaning();
531             } else if ((pendingTasks & SEARCHING) != 0) {
532                 startSearching();
533             } else if ((pendingTasks & REPLACING) != 0) {
534                 startReplacing();
535             } else {
536                 assert pendingTasks == 0;
537             }
538         }
539     }
540
541     /**
542      */

543     private void startSearching() {
544         synchronized (lock) {
545             assert pendingSearchTask != null;
546             
547             notifySearchStarted();
548             
549             ResultModel resultModel = pendingSearchTask.getResultModel();
550             callOnWindowFromAWT("setResultModel", //NOI18N
551
resultModel);
552             resultModelToClean = resultModel;
553
554             if (outputWriterRef != null) {
555                 SearchDisplayer.clearOldOutput(outputWriterRef);
556                 outputWriterRef = null;
557
558                 /*
559                  * The following is necessary because clearing the output window
560                  * activates the output window:
561                  */

562                 activateResultWindow();
563             }
564             
565             RequestProcessor.Task task;
566             task = RequestProcessor.getDefault().create(pendingSearchTask);
567             task.addTaskListener(getTaskListener());
568             task.schedule(0);
569             
570             currentSearchTask = pendingSearchTask;
571             pendingSearchTask = null;
572
573             searchTask = task;
574             pendingTasks &= ~SEARCHING;
575             state = SEARCHING;
576         }
577     }
578     
579     /**
580      */

581     private void startReplacing() {
582         synchronized (lock) {
583             assert pendingReplaceTask != null;
584             
585             RequestProcessor.Task task;
586             task = RequestProcessor.getDefault().create(pendingReplaceTask);
587             task.addTaskListener(getTaskListener());
588             task.schedule(0);
589             
590             currentReplaceTask = pendingReplaceTask;
591             pendingReplaceTask = null;
592
593             replaceTask = task;
594             pendingTasks &= ~REPLACING;
595             state = REPLACING;
596         }
597     }
598     
599     /**
600      */

601     private void startPrintingDetails() {
602         synchronized (lock) {
603             if (outputWriterRef != null) {
604                 SearchDisplayer.clearOldOutput(outputWriterRef);
605                 outputWriterRef = null;
606             }
607
608             RequestProcessor.Task task;
609             task = RequestProcessor.getDefault()
610                    .create(pendingPrintDetailsTask);
611             task.addTaskListener(getTaskListener());
612             task.schedule(0);
613             
614             printDetailsTask = task;
615             pendingTasks &= ~PRINTING_DETAILS;
616             currentPrintDetailsTask = pendingPrintDetailsTask;
617             pendingPrintDetailsTask = null;
618             
619             state = PRINTING_DETAILS;
620         }
621     }
622     
623     /**
624      */

625     private void startCleaning() {
626         synchronized (lock) {
627             Runnable JavaDoc cleaner = new CleanTask(resultModelToClean);
628             resultModelToClean = null;
629             
630             RequestProcessor.Task task;
631             task = RequestProcessor.getDefault().create(cleaner);
632             task.addTaskListener(getTaskListener());
633             task.schedule(0);
634             
635             cleanResultTask = task;
636             pendingTasks &= ~CLEANING_RESULT;
637             state = CLEANING_RESULT;
638         }
639     }
640
641     /**
642      */

643     void stopSearching() {
644         synchronized (lock) {
645             if ((pendingTasks & SEARCHING) != 0) {
646                 pendingTasks &= ~SEARCHING;
647                 pendingSearchTask = null;
648                 notifySearchCancelled();
649             } else if (currentSearchTask != null) {
650                 currentSearchTask.stop();
651             }
652         }
653     }
654
655     /**
656      */

657     private void taskFinished(Task task) {
658         synchronized (lock) {
659             if (moduleBeingUninstalled) {
660                 allTasksFinished();
661                 return;
662             }
663             
664             if (task == searchTask) {
665                 assert state == SEARCHING;
666                 if (currentSearchTask.notifyWhenFinished()) {
667                     if (currentSearchTask.wasInterrupted()) {
668                         notifySearchInterrupted();
669                     } else {
670                         notifySearchFinished();
671                     }
672                 }
673                 currentSearchTask = null;
674                 searchTask = null;
675                 state = NO_TASK;
676             } else if (task == replaceTask) {
677                 assert state == REPLACING;
678                 notifyReplaceFinished();
679                 currentReplaceTask = null;
680                 replaceTask = null;
681                 state = NO_TASK;
682             } else if (task == cleanResultTask) {
683                 assert state == CLEANING_RESULT;
684                 cleanResultTask = null;
685                 state = NO_TASK;
686             } else if (task == printDetailsTask) {
687                 assert state == PRINTING_DETAILS;
688                 notifyPrintingDetailsFinished();
689
690                 outputWriterRef = currentPrintDetailsTask.getOutputWriterRef();
691                 currentPrintDetailsTask = null;
692                 printDetailsTask = null;
693                 state = NO_TASK;
694             } else {
695                 assert false;
696             }
697             processNextPendingTask();
698         }
699     }
700     
701     /**
702      * Called only if the module is about to be uninstalled.
703      * This method is called at the moment that there are no active tasks
704      * (searching, printing details, etc.) and the module is ready for
705      * final cleanup.
706      */

707     private void allTasksFinished() {
708         synchronized (lock) {
709             lock.notifyAll();
710         }
711     }
712     
713     /**
714      * Called from the <code>Installer</code> to notify that the module
715      * is being uninstalled.
716      * Calling this method sets a corresponding flag. When the flag is set,
717      * no new actions (cleaning results, printing details, etc.) are started
718      * and the behaviour is changed so that manipulation with the ResultView
719      * is reduced or eliminated. Also, if no tasks are currently active,
720      * immediatelly closes the results window; otherwise it postpones closing
721      * the window until the currently active task(s) finish.
722      */

723     void doCleanup() {
724         synchronized (lock) {
725             moduleBeingUninstalled = true;
726             if (state != NO_TASK) {
727                 if (currentSearchTask != null) {
728                     currentSearchTask.stop(false);
729                 }
730                 if (currentPrintDetailsTask != null) {
731                     currentPrintDetailsTask.stop();
732                 }
733                 try {
734                     lock.wait(CLEANUP_TIMEOUT_MILLIS);
735                 } catch (InterruptedException JavaDoc ex) {
736                     ErrorManager.getDefault().notify(
737                             ErrorManager.EXCEPTION,
738                             ex);
739                 }
740             }
741             callOnWindowFromAWT("closeResults"); //NOI18N
742
}
743     }
744     
745     /**
746      */

747     private TaskListener getTaskListener() {
748         if (taskListener == null) {
749             taskListener = new MyTaskListener();
750         }
751         return taskListener;
752     }
753
754
755     /**
756      */

757     private class MyTaskListener implements TaskListener {
758
759         /**
760          */

761         MyTaskListener() {
762             super();
763         }
764
765         /**
766          */

767         public void taskFinished(Task task) {
768             synchronized (lock) {
769                 Manager.this.taskFinished(task);
770             }
771         }
772
773     }
774     
775 }
776
Popular Tags