~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to utilities/src/org/netbeans/modules/search/Manager.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
6
 * The contents of this file are subject to the terms of either the GNU
 
7
 * General Public License Version 2 only ("GPL") or the Common
 
8
 * Development and Distribution License("CDDL") (collectively, the
 
9
 * "License"). You may not use this file except in compliance with the
 
10
 * License. You can obtain a copy of the License at
 
11
 * http://www.netbeans.org/cddl-gplv2.html
 
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
 
13
 * specific language governing permissions and limitations under the
 
14
 * License.  When distributing the software, include this License Header
 
15
 * Notice in each file and include the License file at
 
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
 
17
 * particular file as subject to the "Classpath" exception as provided
 
18
 * by Sun in the GPL Version 2 section of the License file that
 
19
 * accompanied this code. If applicable, add the following below the
 
20
 * License Header, with the fields enclosed by brackets [] replaced by
 
21
 * your own identifying information:
 
22
 * "Portions Copyrighted [year] [name of copyright owner]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
26
 * The Original Software is NetBeans. The Initial Developer of the Original
 
27
 * Software is Sun Microsystems, Inc. Portions Copyright 2004-2007 Sun
 
28
 * Microsystems, Inc. All Rights Reserved.
 
29
 *
 
30
 * If you wish your version of this file to be governed by only the CDDL
 
31
 * or only the GPL Version 2, indicate your decision by adding
 
32
 * "[Contributor] elects to include this software in this distribution
 
33
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
 
34
 * single choice of license, a recipient has the option to distribute
 
35
 * your version of this file under either the CDDL, the GPL Version 2 or
 
36
 * to extend the choice of license to its licensees as provided above.
 
37
 * However, if you add GPL Version 2 code and therefore, elected the GPL
 
38
 * Version 2 license, then the option applies only if the new code is
 
39
 * made subject to such option by the copyright holder.
 
40
 */
 
41
 
 
42
package org.netbeans.modules.search;
 
43
 
 
44
import java.awt.EventQueue;
 
45
import java.lang.ref.Reference;
 
46
import java.lang.reflect.InvocationTargetException;
 
47
import java.lang.reflect.Method;
 
48
import java.util.List;
 
49
import org.openide.DialogDisplayer;
 
50
import org.openide.ErrorManager;
 
51
import org.openide.NotifyDescriptor;
 
52
import org.openide.awt.StatusDisplayer;
 
53
import org.openide.util.NbBundle;
 
54
import org.openide.util.RequestProcessor;
 
55
import org.openide.util.Task;
 
56
import org.openide.util.TaskListener;
 
57
import org.openide.windows.OutputWriter;
 
58
import org.openidex.search.SearchType;
 
59
import static org.netbeans.modules.search.ReplaceTask.ResultStatus.SUCCESS;
 
60
import static org.netbeans.modules.search.ReplaceTask.ResultStatus.PRE_CHECK_FAILED;
 
61
import static org.netbeans.modules.search.ReplaceTask.ResultStatus.PROBLEMS_ENCOUNTERED;
 
62
 
 
63
/**
 
64
 * Manager of the Search module's activities.
 
65
 * It knows which tasks are running and manages the module's actions so that
 
66
 * no two conflicting tasks are running at a moment.
 
67
 *
 
68
 * @see <a href="doc-files/manager-state-diagram.png">State diagram</a>
 
69
 * @author  Marian Petras
 
70
 */
 
71
final class Manager {
 
72
    
 
73
    /**
 
74
     * timeout for cleanup in the case that the module is being uninstalled
 
75
     * (in milliseconds)
 
76
     */
 
77
    private static final int CLEANUP_TIMEOUT_MILLIS = 3000;
 
78
    
 
79
 
 
80
    static final int NO_TASK          = 0;
 
81
    
 
82
    static final int SEARCHING        = 0x01;
 
83
    
 
84
    static final int CLEANING_RESULT  = 0x02;
 
85
 
 
86
    static final int PRINTING_DETAILS = 0x04;
 
87
    
 
88
    static final int REPLACING        = 0x08;
 
89
    
 
90
    static final int EVENT_SEARCH_STARTED = 1;
 
91
    
 
92
    static final int EVENT_SEARCH_FINISHED = 2;
 
93
    
 
94
    static final int EVENT_SEARCH_INTERRUPTED = 3;
 
95
    
 
96
    static final int EVENT_SEARCH_CANCELLED = 4;
 
97
    
 
98
    private static final Manager instance = new Manager();
 
99
    
 
100
    
 
101
    private boolean moduleBeingUninstalled = false;
 
102
 
 
103
 
 
104
    private final Object lock = new Object();
 
105
 
 
106
    private int state = NO_TASK;
 
107
 
 
108
    private int pendingTasks = 0;
 
109
    
 
110
    private TaskListener taskListener;
 
111
    
 
112
    private SearchTask currentSearchTask;
 
113
    
 
114
    private SearchTask pendingSearchTask;
 
115
 
 
116
    private SearchTask lastSearchTask;
 
117
    
 
118
    private ReplaceTask currentReplaceTask;
 
119
    
 
120
    private ReplaceTask pendingReplaceTask;
 
121
 
 
122
    private PrintDetailsTask currentPrintDetailsTask;
 
123
    
 
124
    private PrintDetailsTask pendingPrintDetailsTask;
 
125
 
 
126
    private Task searchTask;
 
127
    
 
128
    private Task replaceTask;
 
129
 
 
130
    private Task cleanResultTask;
 
131
    
 
132
    private Task printDetailsTask;
 
133
 
 
134
    private ResultModel resultModelToClean;
 
135
 
 
136
    private boolean searchWindowOpen = false;
 
137
    
 
138
    private Reference<OutputWriter> outputWriterRef;
 
139
    
 
140
 
 
141
    /**
 
142
     */
 
143
    static Manager getInstance() {
 
144
        return instance;
 
145
    }
 
146
 
 
147
    /**
 
148
     */
 
149
    private Manager() { }
 
150
    
 
151
    /*
 
152
     * INVARIANTS:
 
153
     * #1: If the Search Results window is open, its root node displays:
 
154
     *     - if the search task is in progress:
 
155
     *             - summary of results
 
156
     *     - if the Search module is inactive:
 
157
     *             - summary of current results (continuously updated)
 
158
     *     - if the search task in scheduled but another task is blocking it:
 
159
     *             - name of the current task blocking the search
 
160
     * #2: At most one result model exists at a single moment.
 
161
     */
 
162
 
 
163
    /**
 
164
     */
 
165
    void scheduleSearchTask(SearchTask task) {
 
166
        assert EventQueue.isDispatchThread();
 
167
        
 
168
        synchronized (lock) {
 
169
            ResultView.getInstance().setResultModel(null);
 
170
            if (currentSearchTask != null) {
 
171
                currentSearchTask.stop(false);
 
172
            }
 
173
            if (resultModelToClean != null) {
 
174
                pendingTasks |= CLEANING_RESULT;
 
175
            }
 
176
            pendingTasks |= SEARCHING;
 
177
            pendingSearchTask = task;
 
178
            lastSearchTask = task;
 
179
            if (state == NO_TASK) {
 
180
                processNextPendingTask();
 
181
            } else {
 
182
                notifySearchPending(state);                     //invariant #1
 
183
            }
 
184
        }
 
185
    }
 
186
    
 
187
    /**
 
188
     */
 
189
    void scheduleReplaceTask(ReplaceTask task) {
 
190
        assert EventQueue.isDispatchThread();
 
191
        
 
192
        synchronized (lock) {
 
193
            assert (state == NO_TASK) && (pendingTasks == 0);
 
194
            
 
195
            pendingTasks |= REPLACING;
 
196
            pendingReplaceTask = task;
 
197
            processNextPendingTask();
 
198
        }
 
199
    }
 
200
    
 
201
    /**
 
202
     */
 
203
    void scheduleSearchTaskRerun() {
 
204
        assert EventQueue.isDispatchThread();
 
205
        
 
206
        synchronized (lock) {
 
207
            SearchTask newSearchTask = lastSearchTask.createNewGeneration();
 
208
            lastSearchTask = null;
 
209
            scheduleSearchTask(newSearchTask);
 
210
        }
 
211
    }
 
212
    
 
213
    /**
 
214
     */
 
215
    void schedulePrintingDetails(Object[] matchingObjects,
 
216
                                 BasicSearchCriteria basicCriteria,
 
217
                                 List<SearchType> searchTypes) {
 
218
        synchronized (lock) {
 
219
            assert state == NO_TASK;
 
220
            pendingTasks |= PRINTING_DETAILS;
 
221
            
 
222
            pendingPrintDetailsTask = new PrintDetailsTask(
 
223
                    matchingObjects, basicCriteria, searchTypes);
 
224
            processNextPendingTask();
 
225
        }
 
226
    }
 
227
    
 
228
    /**
 
229
     * Queries whether the user should be allowed to initiate a new search.
 
230
     * For example, the user should not be allowed to do so if the last
 
231
     * replace action has not finished yet.
 
232
     * 
 
233
     * @return  message to the user, describing the reason why a new search
 
234
     *          cannot be started, or {@code null} if there is no such reason
 
235
     *          (i.e. if a new search may be started)
 
236
     */
 
237
    String mayStartSearching() {
 
238
        boolean replacing;
 
239
        
 
240
        synchronized (lock) {
 
241
            replacing = (state == REPLACING);
 
242
        }
 
243
        
 
244
        String msgKey = replacing ? "MSG_Cannot_start_search__replacing"//NOI18N
 
245
                                  : null;
 
246
        return (msgKey != null) ? NbBundle.getMessage(getClass(), msgKey)
 
247
                                : null;
 
248
    }
 
249
    
 
250
    /**
 
251
     */
 
252
    private void notifySearchStarted() {
 
253
        notifySearchTaskStateChange(EVENT_SEARCH_STARTED);
 
254
    }
 
255
    
 
256
    /**
 
257
     */
 
258
    private void notifySearchFinished() {
 
259
        notifySearchTaskStateChange(EVENT_SEARCH_FINISHED);
 
260
    }
 
261
    
 
262
    /**
 
263
     */
 
264
    private void notifySearchInterrupted() {
 
265
        notifySearchTaskStateChange(EVENT_SEARCH_INTERRUPTED);
 
266
    }
 
267
    
 
268
    /**
 
269
     */
 
270
    private void notifySearchCancelled() {
 
271
        notifySearchTaskStateChange(EVENT_SEARCH_CANCELLED);
 
272
    }
 
273
    
 
274
    /**
 
275
     * Notifies the result window of a search task's state change.
 
276
     *
 
277
     * @param  changeType  constant describing what happened
 
278
     *                     - one of the EVENT_xxx constants
 
279
     */
 
280
    private void notifySearchTaskStateChange(final int changeType) {
 
281
        synchronized (lock) {
 
282
            if (!searchWindowOpen) {
 
283
                return;
 
284
            }
 
285
        }
 
286
        callOnWindowFromAWT("searchTaskStateChanged",                   //NOI18N
 
287
                          new Integer(changeType));
 
288
    }
 
289
 
 
290
    /**
 
291
     */
 
292
    private void notifySearchPending(final int blockingTask) {
 
293
        if (!searchWindowOpen) {
 
294
            return;
 
295
        }
 
296
        callOnWindowFromAWT("notifySearchPending",                      //NOI18N
 
297
                          new Integer(blockingTask));
 
298
    }
 
299
    
 
300
    /**
 
301
     */
 
302
    private void notifyReplaceFinished() {
 
303
        assert Thread.holdsLock(lock);
 
304
        assert currentReplaceTask != null;
 
305
        
 
306
        ReplaceTask.ResultStatus resultStatus
 
307
                = currentReplaceTask.getResultStatus();
 
308
        if (resultStatus == SUCCESS) {
 
309
            StatusDisplayer.getDefault().setStatusText(
 
310
                    NbBundle.getMessage(getClass(), "MSG_Success"));    //NOI18N
 
311
            if (searchWindowOpen) {
 
312
                callOnWindowFromAWT("closeAndSendFocusToEditor", false);//NOI18N
 
313
            }
 
314
        } else {
 
315
            String msgKey = (resultStatus == PRE_CHECK_FAILED)
 
316
                            ? "MSG_Issues_found_during_precheck"        //NOI18N
 
317
                            : "MSG_Issues_found_during_replace";        //NOI18N
 
318
            String title = NbBundle.getMessage(getClass(), msgKey);
 
319
            displayIssuesFromAWT(title,
 
320
                                 currentReplaceTask.getProblems(),
 
321
                                 resultStatus != PRE_CHECK_FAILED);
 
322
            if (resultStatus == PRE_CHECK_FAILED) {
 
323
                offerRescanAfterIssuesFound();
 
324
            }
 
325
        }
 
326
    }
 
327
    
 
328
    /**
 
329
     */
 
330
    private void offerRescanAfterIssuesFound() {
 
331
        assert Thread.holdsLock(lock);
 
332
        assert currentReplaceTask != null;
 
333
        
 
334
        String msg = NbBundle.getMessage(getClass(),
 
335
                                         "MSG_IssuesFound_Rescan_");    //NOI18N
 
336
        NotifyDescriptor nd = new NotifyDescriptor.Message(
 
337
                                            msg,
 
338
                                            NotifyDescriptor.QUESTION_MESSAGE);
 
339
        String rerunOption = NbBundle.getMessage(getClass(),
 
340
                                                 "LBL_Rerun");          //NOI18N
 
341
        nd.setOptions(new Object[] {rerunOption,
 
342
                                    NotifyDescriptor.CANCEL_OPTION});
 
343
        Object dlgResult = DialogDisplayer.getDefault().notify(nd);
 
344
        if (rerunOption.equals(dlgResult)) {
 
345
            /*
 
346
             * The rescan method calls 'scheduleSearchTaskRerun()' on this.
 
347
             * But it will wait until 'taskFinished()' returns, which is
 
348
             * exactly what we need to keep consistency of the manager's fields
 
349
             * like 'currentReplaceTask', 'replaceTask' and 'state'.
 
350
             * Using this mechanism also requires that, when sending a method
 
351
             * to the EventQueue thread, we use invokeLater(...) and not
 
352
             * invokeAndWait(...).
 
353
             */
 
354
            callOnWindowFromAWT("rescan", false);                       //NOI18N
 
355
        }
 
356
    }
 
357
    
 
358
    /**
 
359
     */
 
360
    private void notifyPrintingDetailsFinished() {
 
361
        if (!searchWindowOpen) {
 
362
            return;
 
363
        }
 
364
        callOnWindowFromAWT("showAllDetailsFinished");                  //NOI18N
 
365
    }
 
366
 
 
367
    /**
 
368
     */
 
369
    private void activateResultWindow() {
 
370
        Method theMethod;
 
371
        try {
 
372
            theMethod = ResultView.class
 
373
                        .getMethod("requestActive", new Class[0]);      //NOI18N
 
374
        } catch (NoSuchMethodException ex) {
 
375
            throw new IllegalArgumentException();
 
376
        }
 
377
        callOnWindowFromAWT(theMethod, null);
 
378
    }
 
379
    
 
380
    /**
 
381
     */
 
382
    private void displayIssuesFromAWT(String title,
 
383
                                      String[] issues,
 
384
                                      boolean att) {
 
385
        Method theMethod;
 
386
        try {
 
387
            theMethod = ResultView.class.getDeclaredMethod(
 
388
                                                "displayIssuesToUser",  //NOI18N
 
389
                                                String.class,
 
390
                                                String[].class,
 
391
                                                Boolean.TYPE);
 
392
        } catch (NoSuchMethodException ex) {
 
393
            throw new IllegalStateException(ex);
 
394
        }
 
395
        callOnWindowFromAWT(theMethod,
 
396
                            new Object[] {title, issues, Boolean.valueOf(att)},
 
397
                            false);
 
398
    }
 
399
    
 
400
    /**
 
401
     * Calls a given method on the Search Results window, from the AWT thread.
 
402
     *
 
403
     * @param  methodName  name of the method to be called
 
404
     */
 
405
    private void callOnWindowFromAWT(final String methodName) {
 
406
        callOnWindowFromAWT(methodName, true);
 
407
    }
 
408
    
 
409
    /**
 
410
     */
 
411
    private void callOnWindowFromAWT(final String methodName,
 
412
                                     final boolean wait) {
 
413
        Method theMethod;
 
414
        try {
 
415
            theMethod = ResultView.class
 
416
                        .getDeclaredMethod(methodName, new Class[0]);
 
417
        } catch (NoSuchMethodException ex) {
 
418
            throw new IllegalArgumentException();
 
419
        }
 
420
        callOnWindowFromAWT(theMethod, null, wait);
 
421
    }
 
422
    
 
423
    /**
 
424
     * Calls a given method on the Search Results window, from the AWT thread.
 
425
     *
 
426
     * @param  methodName  name of the method to be called
 
427
     * @param  param  parameter to be passed to the method
 
428
     */
 
429
    private void callOnWindowFromAWT(final String methodName,
 
430
                                     final Object param) {
 
431
        callOnWindowFromAWT(methodName, param, true);
 
432
    }
 
433
    
 
434
    /**
 
435
     */
 
436
    private void callOnWindowFromAWT(final String methodName,
 
437
                                     final Object param,
 
438
                                     final boolean wait) {
 
439
        Method theMethod = null;
 
440
        Method[] methods = ResultView.class.getDeclaredMethods();
 
441
        for (int i = 0; i < methods.length; i++) {
 
442
            Method method = methods[i];
 
443
            if (method.getName().equals(methodName)) {
 
444
                Class[] parameterTypes = method.getParameterTypes();
 
445
                if (parameterTypes.length == 1) {
 
446
                    Class paramType = parameterTypes[0];
 
447
                    if ((param == null
 
448
                               && !paramType.isPrimitive())
 
449
                            || (paramType == Integer.TYPE)
 
450
                               && (param instanceof Integer)
 
451
                            || parameterTypes[0].isInstance(param)) {
 
452
                        theMethod = method;
 
453
                    }
 
454
                }
 
455
            }
 
456
        }
 
457
        if (theMethod == null) {
 
458
            throw new IllegalArgumentException();
 
459
        }
 
460
        callOnWindowFromAWT(theMethod, new Object[] {param}, wait);
 
461
    }
 
462
    
 
463
    /**
 
464
     */
 
465
    private void callOnWindowFromAWT(final Method method,
 
466
                                     final Object[] params) {
 
467
        callOnWindowFromAWT(method, params, true);
 
468
    }
 
469
    
 
470
    /**
 
471
     */
 
472
    private void callOnWindowFromAWT(final Method method,
 
473
                                     final Object[] params,
 
474
                                     final boolean wait) {
 
475
        Runnable runnable = new Runnable() {
 
476
            public void run() {
 
477
                final ResultView resultViewInstance = ResultView.getInstance();
 
478
                try {
 
479
                    method.invoke(resultViewInstance, params);
 
480
                } catch (Exception ex) {
 
481
                    ErrorManager.getDefault().notify(ex);
 
482
                }
 
483
            }
 
484
        };
 
485
        if (EventQueue.isDispatchThread()) {
 
486
            runnable.run();
 
487
        } else {
 
488
            if (wait) {
 
489
                try {
 
490
                    EventQueue.invokeAndWait(runnable);
 
491
                } catch (InvocationTargetException ex1) {
 
492
                    ErrorManager.getDefault().notify(ex1);
 
493
                } catch (Exception ex2) {
 
494
                    ErrorManager.getDefault().notify(ErrorManager.ERROR, ex2);
 
495
                }
 
496
            } else {
 
497
                EventQueue.invokeLater(runnable);
 
498
            }
 
499
        }
 
500
    }
 
501
    
 
502
    /**
 
503
     */
 
504
    void searchWindowOpened() {
 
505
        synchronized (lock) {
 
506
            searchWindowOpen = true;
 
507
        }
 
508
    }
 
509
 
 
510
    /**
 
511
     */
 
512
    void searchWindowClosed() {
 
513
        assert EventQueue.isDispatchThread();
 
514
        
 
515
        synchronized (lock) {
 
516
            searchWindowOpen = false;
 
517
            
 
518
            if (moduleBeingUninstalled) {
 
519
                return;
 
520
            }
 
521
            
 
522
            if (currentSearchTask != null) {
 
523
                currentSearchTask.stop(false);
 
524
            }
 
525
            if (resultModelToClean != null) {
 
526
                pendingTasks |= CLEANING_RESULT;
 
527
            }
 
528
            pendingTasks &= ~SEARCHING;
 
529
            pendingSearchTask = null;
 
530
            lastSearchTask = null;
 
531
            if (state == NO_TASK) {
 
532
                processNextPendingTask();
 
533
            }
 
534
        }
 
535
    }
 
536
    
 
537
    /**
 
538
     */
 
539
    private void processNextPendingTask() {
 
540
        synchronized (lock) {
 
541
            assert state == NO_TASK;
 
542
            if (resultModelToClean == null) {
 
543
                pendingTasks &= ~CLEANING_RESULT;
 
544
            }
 
545
            if ((pendingTasks & PRINTING_DETAILS) != 0) {
 
546
                if ((pendingTasks & SEARCHING) != 0) {
 
547
                    notifySearchPending(PRINTING_DETAILS);      //invariant #1
 
548
                }
 
549
                startPrintingDetails();
 
550
            } else if ((pendingTasks & CLEANING_RESULT) != 0) {
 
551
                if ((pendingTasks & SEARCHING) != 0) {
 
552
                    notifySearchPending(CLEANING_RESULT);       //invariant #1
 
553
                }
 
554
                startCleaning();
 
555
            } else if ((pendingTasks & SEARCHING) != 0) {
 
556
                startSearching();
 
557
            } else if ((pendingTasks & REPLACING) != 0) {
 
558
                startReplacing();
 
559
            } else {
 
560
                assert pendingTasks == 0;
 
561
            }
 
562
        }
 
563
    }
 
564
 
 
565
    /**
 
566
     */
 
567
    private void startSearching() {
 
568
        synchronized (lock) {
 
569
            assert pendingSearchTask != null;
 
570
            
 
571
            notifySearchStarted();
 
572
            
 
573
            ResultModel resultModel = pendingSearchTask.getResultModel();
 
574
            callOnWindowFromAWT("setResultModel",                       //NOI18N
 
575
                                resultModel);
 
576
            resultModelToClean = resultModel;
 
577
 
 
578
            if (outputWriterRef != null) {
 
579
                SearchDisplayer.clearOldOutput(outputWriterRef);
 
580
                outputWriterRef = null;
 
581
 
 
582
                /*
 
583
                 * The following is necessary because clearing the output window
 
584
                 * activates the output window:
 
585
                 */
 
586
                activateResultWindow();
 
587
            }
 
588
            
 
589
            RequestProcessor.Task task;
 
590
            task = RequestProcessor.getDefault().create(pendingSearchTask);
 
591
            task.addTaskListener(getTaskListener());
 
592
            task.schedule(0);
 
593
            
 
594
            currentSearchTask = pendingSearchTask;
 
595
            pendingSearchTask = null;
 
596
 
 
597
            searchTask = task;
 
598
            pendingTasks &= ~SEARCHING;
 
599
            state = SEARCHING;
 
600
        }
 
601
    }
 
602
    
 
603
    /**
 
604
     */
 
605
    private void startReplacing() {
 
606
        synchronized (lock) {
 
607
            assert pendingReplaceTask != null;
 
608
            
 
609
            RequestProcessor.Task task;
 
610
            task = RequestProcessor.getDefault().create(pendingReplaceTask);
 
611
            task.addTaskListener(getTaskListener());
 
612
            task.schedule(0);
 
613
            
 
614
            currentReplaceTask = pendingReplaceTask;
 
615
            pendingReplaceTask = null;
 
616
 
 
617
            replaceTask = task;
 
618
            pendingTasks &= ~REPLACING;
 
619
            state = REPLACING;
 
620
        }
 
621
    }
 
622
    
 
623
    /**
 
624
     */
 
625
    private void startPrintingDetails() {
 
626
        synchronized (lock) {
 
627
            if (outputWriterRef != null) {
 
628
                SearchDisplayer.clearOldOutput(outputWriterRef);
 
629
                outputWriterRef = null;
 
630
            }
 
631
 
 
632
            RequestProcessor.Task task;
 
633
            task = RequestProcessor.getDefault()
 
634
                   .create(pendingPrintDetailsTask);
 
635
            task.addTaskListener(getTaskListener());
 
636
            task.schedule(0);
 
637
            
 
638
            printDetailsTask = task;
 
639
            pendingTasks &= ~PRINTING_DETAILS;
 
640
            currentPrintDetailsTask = pendingPrintDetailsTask;
 
641
            pendingPrintDetailsTask = null;
 
642
            
 
643
            state = PRINTING_DETAILS;
 
644
        }
 
645
    }
 
646
    
 
647
    /**
 
648
     */
 
649
    private void startCleaning() {
 
650
        synchronized (lock) {
 
651
            Runnable cleaner = new CleanTask(resultModelToClean);
 
652
            resultModelToClean = null;
 
653
            
 
654
            RequestProcessor.Task task;
 
655
            task = RequestProcessor.getDefault().create(cleaner);
 
656
            task.addTaskListener(getTaskListener());
 
657
            task.schedule(0);
 
658
            
 
659
            cleanResultTask = task;
 
660
            pendingTasks &= ~CLEANING_RESULT;
 
661
            state = CLEANING_RESULT;
 
662
        }
 
663
    }
 
664
 
 
665
    /**
 
666
     */
 
667
    void stopSearching() {
 
668
        synchronized (lock) {
 
669
            if ((pendingTasks & SEARCHING) != 0) {
 
670
                pendingTasks &= ~SEARCHING;
 
671
                pendingSearchTask = null;
 
672
                notifySearchCancelled();
 
673
            } else if (currentSearchTask != null) {
 
674
                currentSearchTask.stop();
 
675
            }
 
676
        }
 
677
    }
 
678
 
 
679
    /**
 
680
     */
 
681
    private void taskFinished(Task task) {
 
682
        synchronized (lock) {
 
683
            if (moduleBeingUninstalled) {
 
684
                allTasksFinished();
 
685
                return;
 
686
            }
 
687
            
 
688
            if (task == searchTask) {
 
689
                assert state == SEARCHING;
 
690
                if (currentSearchTask.notifyWhenFinished()) {
 
691
                    if (currentSearchTask.wasInterrupted()) {
 
692
                        notifySearchInterrupted();
 
693
                    } else {
 
694
                        notifySearchFinished();
 
695
                    }
 
696
                }
 
697
                currentSearchTask = null;
 
698
                searchTask = null;
 
699
                state = NO_TASK;
 
700
            } else if (task == replaceTask) {
 
701
                assert state == REPLACING;
 
702
                notifyReplaceFinished();
 
703
                currentReplaceTask = null;
 
704
                replaceTask = null;
 
705
                state = NO_TASK;
 
706
            } else if (task == cleanResultTask) {
 
707
                assert state == CLEANING_RESULT;
 
708
                cleanResultTask = null;
 
709
                state = NO_TASK;
 
710
            } else if (task == printDetailsTask) {
 
711
                assert state == PRINTING_DETAILS;
 
712
                notifyPrintingDetailsFinished();
 
713
 
 
714
                outputWriterRef = currentPrintDetailsTask.getOutputWriterRef();
 
715
                currentPrintDetailsTask = null;
 
716
                printDetailsTask = null;
 
717
                state = NO_TASK;
 
718
            } else {
 
719
                assert false;
 
720
            }
 
721
            processNextPendingTask();
 
722
        }
 
723
    }
 
724
    
 
725
    /**
 
726
     * Called only if the module is about to be uninstalled.
 
727
     * This method is called at the moment that there are no active tasks
 
728
     * (searching, printing details, etc.) and the module is ready for
 
729
     * final cleanup.
 
730
     */
 
731
    private void allTasksFinished() {
 
732
        synchronized (lock) {
 
733
            lock.notifyAll();
 
734
        }
 
735
    }
 
736
    
 
737
    /**
 
738
     * Called from the <code>Installer</code> to notify that the module
 
739
     * is being uninstalled.
 
740
     * Calling this method sets a corresponding flag. When the flag is set,
 
741
     * no new actions (cleaning results, printing details, etc.) are started
 
742
     * and the behaviour is changed so that manipulation with the ResultView
 
743
     * is reduced or eliminated. Also, if no tasks are currently active,
 
744
     * immediatelly closes the results window; otherwise it postpones closing
 
745
     * the window until the currently active task(s) finish.
 
746
     */
 
747
    void doCleanup() {
 
748
        synchronized (lock) {
 
749
            moduleBeingUninstalled = true;
 
750
            if (state != NO_TASK) {
 
751
                if (currentSearchTask != null) {
 
752
                    currentSearchTask.stop(false);
 
753
                }
 
754
                if (currentPrintDetailsTask != null) {
 
755
                    currentPrintDetailsTask.stop();
 
756
                }
 
757
                try {
 
758
                    lock.wait(CLEANUP_TIMEOUT_MILLIS);
 
759
                } catch (InterruptedException ex) {
 
760
                    ErrorManager.getDefault().notify(
 
761
                            ErrorManager.EXCEPTION,
 
762
                            ex);
 
763
                }
 
764
            }
 
765
            callOnWindowFromAWT("closeResults");                        //NOI18N
 
766
        }
 
767
    }
 
768
    
 
769
    /**
 
770
     */
 
771
    private TaskListener getTaskListener() {
 
772
        if (taskListener == null) {
 
773
            taskListener = new MyTaskListener();
 
774
        }
 
775
        return taskListener;
 
776
    }
 
777
 
 
778
 
 
779
    /**
 
780
     */
 
781
    private class MyTaskListener implements TaskListener {
 
782
 
 
783
        /**
 
784
         */
 
785
        MyTaskListener() {
 
786
            super();
 
787
        }
 
788
 
 
789
        /**
 
790
         */
 
791
        public void taskFinished(Task task) {
 
792
            synchronized (lock) {
 
793
                Manager.this.taskFinished(task);
 
794
            }
 
795
        }
 
796
 
 
797
    }
 
798
    
 
799
}