129
|
1 /*******************************************************************************
|
|
2 * Copyright (c) 2000, 2008 IBM Corporation and others.
|
|
3 * All rights reserved. This program and the accompanying materials
|
|
4 * are made available under the terms of the Eclipse Public License v1.0
|
|
5 * which accompanies this distribution, and is available at
|
|
6 * http://www.eclipse.org/legal/epl-v10.html
|
|
7 *
|
|
8 * Contributors:
|
|
9 * IBM Corporation - initial API and implementation
|
|
10 * Port to the D programming language:
|
|
11 * Frank Benoit <benoit@tionex.de>
|
|
12 *******************************************************************************/
|
|
13 module dwtx.jface.text.reconciler.AbstractReconciler;
|
|
14
|
131
|
15 import dwtx.jface.text.reconciler.IReconciler; // packageimport
|
|
16 import dwtx.jface.text.reconciler.DirtyRegionQueue; // packageimport
|
|
17 import dwtx.jface.text.reconciler.IReconcilingStrategy; // packageimport
|
|
18 import dwtx.jface.text.reconciler.AbstractReconcileStep; // packageimport
|
|
19 import dwtx.jface.text.reconciler.IReconcilingStrategyExtension; // packageimport
|
|
20 import dwtx.jface.text.reconciler.MonoReconciler; // packageimport
|
|
21 import dwtx.jface.text.reconciler.IReconcileStep; // packageimport
|
|
22 import dwtx.jface.text.reconciler.Reconciler; // packageimport
|
|
23 import dwtx.jface.text.reconciler.IReconcilableModel; // packageimport
|
|
24 import dwtx.jface.text.reconciler.DirtyRegion; // packageimport
|
|
25 import dwtx.jface.text.reconciler.IReconcileResult; // packageimport
|
|
26 import dwtx.jface.text.reconciler.IReconcilerExtension; // packageimport
|
|
27
|
|
28
|
129
|
29 import dwt.dwthelper.utils;
|
|
30
|
|
31
|
|
32 import dwtx.core.runtime.Assert;
|
|
33 import dwtx.core.runtime.IProgressMonitor;
|
|
34 import dwtx.core.runtime.NullProgressMonitor;
|
|
35 import dwtx.jface.text.DocumentEvent;
|
|
36 import dwtx.jface.text.IDocument;
|
|
37 import dwtx.jface.text.IDocumentListener;
|
|
38 import dwtx.jface.text.ITextInputListener;
|
|
39 import dwtx.jface.text.ITextViewer;
|
|
40
|
|
41
|
|
42 /**
|
|
43 * Abstract implementation of {@link IReconciler}. The reconciler
|
|
44 * listens to input document changes as well as changes of
|
|
45 * the input document of the text viewer it is installed on. Depending on
|
|
46 * its configuration it manages the received change notifications in a
|
|
47 * queue folding neighboring or overlapping changes together. The reconciler
|
|
48 * processes the dirty regions as a background activity after having waited for further
|
|
49 * changes for the configured duration of time. A reconciler is started using the
|
|
50 * {@link #install(ITextViewer)} method. As a first step {@link #initialProcess()} is
|
|
51 * executed in the background. Then, the reconciling thread waits for changes that
|
|
52 * need to be reconciled. A reconciler can be resumed by calling {@link #forceReconciling()}
|
|
53 * independent from the existence of actual changes. This mechanism is for subclasses only.
|
|
54 * It is the clients responsibility to stop a reconciler using its {@link #uninstall()}
|
|
55 * method. Unstopped reconcilers do not free their resources.
|
|
56 * <p>
|
|
57 * It is subclass responsibility to specify how dirty regions are processed.
|
|
58 * </p>
|
|
59 *
|
|
60 * @see dwtx.jface.text.IDocumentListener
|
|
61 * @see dwtx.jface.text.ITextInputListener
|
|
62 * @see dwtx.jface.text.reconciler.DirtyRegion
|
|
63 * @since 2.0
|
|
64 */
|
|
65 abstract public class AbstractReconciler : IReconciler {
|
|
66
|
|
67
|
|
68 /**
|
|
69 * Background thread for the reconciling activity.
|
|
70 */
|
|
71 class BackgroundThread : Thread {
|
|
72
|
|
73 /** Has the reconciler been canceled. */
|
|
74 private bool fCanceled= false;
|
|
75 /** Has the reconciler been reset. */
|
|
76 private bool fReset= false;
|
|
77 /** Some changes need to be processed. */
|
|
78 private bool fIsDirty= false;
|
|
79 /** Is a reconciling strategy active. */
|
|
80 private bool fIsActive= false;
|
|
81
|
|
82 /**
|
|
83 * Creates a new background thread. The thread
|
|
84 * runs with minimal priority.
|
|
85 *
|
|
86 * @param name the thread's name
|
|
87 */
|
|
88 public BackgroundThread(String name) {
|
|
89 super(name);
|
|
90 setPriority(Thread.MIN_PRIORITY);
|
|
91 setDaemon(true);
|
|
92 }
|
|
93
|
|
94 /**
|
|
95 * Returns whether a reconciling strategy is active right now.
|
|
96 *
|
|
97 * @return <code>true</code> if a activity is active
|
|
98 */
|
|
99 public bool isActive() {
|
|
100 return fIsActive;
|
|
101 }
|
|
102
|
|
103 /**
|
|
104 * Returns whether some changes need to be processed.
|
|
105 *
|
|
106 * @return <code>true</code> if changes wait to be processed
|
|
107 * @since 3.0
|
|
108 */
|
|
109 public synchronized bool isDirty() {
|
|
110 return fIsDirty;
|
|
111 }
|
|
112
|
|
113 /**
|
|
114 * Cancels the background thread.
|
|
115 */
|
|
116 public void cancel() {
|
|
117 fCanceled= true;
|
|
118 IProgressMonitor pm= fProgressMonitor;
|
|
119 if (pm !is null)
|
|
120 pm.setCanceled(true);
|
|
121 synchronized (fDirtyRegionQueue) {
|
|
122 fDirtyRegionQueue.notifyAll();
|
|
123 }
|
|
124 }
|
|
125
|
|
126 /**
|
|
127 * Suspends the caller of this method until this background thread has
|
|
128 * emptied the dirty region queue.
|
|
129 */
|
|
130 public void suspendCallerWhileDirty() {
|
|
131 bool isDirty;
|
|
132 do {
|
|
133 synchronized (fDirtyRegionQueue) {
|
|
134 isDirty= fDirtyRegionQueue.getSize() > 0;
|
|
135 if (isDirty) {
|
|
136 try {
|
|
137 fDirtyRegionQueue.wait();
|
|
138 } catch (InterruptedException x) {
|
|
139 }
|
|
140 }
|
|
141 }
|
|
142 } while (isDirty);
|
|
143 }
|
|
144
|
|
145 /**
|
|
146 * Reset the background thread as the text viewer has been changed,
|
|
147 */
|
|
148 public void reset() {
|
|
149
|
|
150 if (fDelay > 0) {
|
|
151
|
|
152 synchronized (this) {
|
|
153 fIsDirty= true;
|
|
154 fReset= true;
|
|
155 }
|
|
156
|
|
157 } else {
|
|
158
|
|
159 synchronized (this) {
|
|
160 fIsDirty= true;
|
|
161 }
|
|
162
|
|
163 synchronized (fDirtyRegionQueue) {
|
|
164 fDirtyRegionQueue.notifyAll();
|
|
165 }
|
|
166 }
|
|
167
|
|
168 reconcilerReset();
|
|
169 }
|
|
170
|
|
171 /**
|
|
172 * The background activity. Waits until there is something in the
|
|
173 * queue managing the changes that have been applied to the text viewer.
|
|
174 * Removes the first change from the queue and process it.
|
|
175 * <p>
|
|
176 * Calls {@link AbstractReconciler#initialProcess()} on entrance.
|
|
177 * </p>
|
|
178 */
|
|
179 public void run() {
|
|
180
|
|
181 synchronized (fDirtyRegionQueue) {
|
|
182 try {
|
|
183 fDirtyRegionQueue.wait(fDelay);
|
|
184 } catch (InterruptedException x) {
|
|
185 }
|
|
186 }
|
|
187
|
|
188 if (fCanceled)
|
|
189 return;
|
|
190
|
|
191 initialProcess();
|
|
192
|
|
193 while (!fCanceled) {
|
|
194
|
|
195 synchronized (fDirtyRegionQueue) {
|
|
196 try {
|
|
197 fDirtyRegionQueue.wait(fDelay);
|
|
198 } catch (InterruptedException x) {
|
|
199 }
|
|
200 }
|
|
201
|
|
202 if (fCanceled)
|
|
203 break;
|
|
204
|
|
205 if (!isDirty())
|
|
206 continue;
|
|
207
|
|
208 synchronized (this) {
|
|
209 if (fReset) {
|
|
210 fReset= false;
|
|
211 continue;
|
|
212 }
|
|
213 }
|
|
214
|
|
215 DirtyRegion r= null;
|
|
216 synchronized (fDirtyRegionQueue) {
|
|
217 r= fDirtyRegionQueue.removeNextDirtyRegion();
|
|
218 }
|
|
219
|
|
220 fIsActive= true;
|
|
221
|
|
222 fProgressMonitor.setCanceled(false);
|
|
223
|
|
224 process(r);
|
|
225
|
|
226 synchronized (fDirtyRegionQueue) {
|
|
227 if (0 is fDirtyRegionQueue.getSize()) {
|
|
228 synchronized (this) {
|
|
229 fIsDirty= fProgressMonitor.isCanceled();
|
|
230 }
|
|
231 fDirtyRegionQueue.notifyAll();
|
|
232 }
|
|
233 }
|
|
234
|
|
235 fIsActive= false;
|
|
236 }
|
|
237 }
|
|
238 }
|
|
239
|
|
240 /**
|
|
241 * Internal document listener and text input listener.
|
|
242 */
|
|
243 class Listener : IDocumentListener, ITextInputListener {
|
|
244
|
|
245 /*
|
|
246 * @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
|
|
247 */
|
|
248 public void documentAboutToBeChanged(DocumentEvent e) {
|
|
249 }
|
|
250
|
|
251 /*
|
|
252 * @see IDocumentListener#documentChanged(DocumentEvent)
|
|
253 */
|
|
254 public void documentChanged(DocumentEvent e) {
|
|
255
|
|
256 if (!fThread.isDirty() && fThread.isAlive()) {
|
|
257 if (!fIsAllowedToModifyDocument && Thread.currentThread() is fThread)
|
|
258 throw new UnsupportedOperationException("The reconciler thread is not allowed to modify the document"); //$NON-NLS-1$
|
|
259 aboutToBeReconciled();
|
|
260 }
|
|
261
|
|
262 /*
|
|
263 * The second OR condition handles the case when the document
|
|
264 * gets changed while still inside initialProcess().
|
|
265 */
|
|
266 if (fThread.isActive() || fThread.isDirty() && fThread.isAlive())
|
|
267 fProgressMonitor.setCanceled(true);
|
|
268
|
|
269 if (fIsIncrementalReconciler)
|
|
270 createDirtyRegion(e);
|
|
271
|
|
272 fThread.reset();
|
|
273
|
|
274 }
|
|
275
|
|
276 /*
|
|
277 * @see ITextInputListener#inputDocumentAboutToBeChanged(IDocument, IDocument)
|
|
278 */
|
|
279 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
|
|
280
|
|
281 if (oldInput is fDocument) {
|
|
282
|
|
283 if (fDocument !is null)
|
|
284 fDocument.removeDocumentListener(this);
|
|
285
|
|
286 if (fIsIncrementalReconciler) {
|
|
287 synchronized (fDirtyRegionQueue) {
|
|
288 fDirtyRegionQueue.purgeQueue();
|
|
289 }
|
|
290 if (fDocument !is null && fDocument.getLength() > 0 && fThread.isDirty() && fThread.isAlive()) {
|
|
291 DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), ""); //$NON-NLS-1$
|
|
292 createDirtyRegion(e);
|
|
293 fThread.reset();
|
|
294 fThread.suspendCallerWhileDirty();
|
|
295 }
|
|
296 }
|
|
297
|
|
298 fDocument= null;
|
|
299 }
|
|
300 }
|
|
301
|
|
302 /*
|
|
303 * @see ITextInputListener#inputDocumentChanged(IDocument, IDocument)
|
|
304 */
|
|
305 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) {
|
|
306
|
|
307 fDocument= newInput;
|
|
308 if (fDocument is null)
|
|
309 return;
|
|
310
|
|
311
|
|
312 reconcilerDocumentChanged(fDocument);
|
|
313
|
|
314 fDocument.addDocumentListener(this);
|
|
315
|
|
316 if (!fThread.isDirty())
|
|
317 aboutToBeReconciled();
|
|
318
|
|
319 startReconciling();
|
|
320 }
|
|
321 }
|
|
322
|
|
323 /** Queue to manage the changes applied to the text viewer. */
|
|
324 private DirtyRegionQueue fDirtyRegionQueue;
|
|
325 /** The background thread. */
|
|
326 private BackgroundThread fThread;
|
|
327 /** Internal document and text input listener. */
|
|
328 private Listener fListener;
|
|
329 /** The background thread delay. */
|
|
330 private int fDelay= 500;
|
|
331 /** Are there incremental reconciling strategies? */
|
|
332 private bool fIsIncrementalReconciler= true;
|
|
333 /** The progress monitor used by this reconciler. */
|
|
334 private IProgressMonitor fProgressMonitor;
|
|
335 /**
|
|
336 * Tells whether this reconciler is allowed to modify the document.
|
|
337 * @since 3.2
|
|
338 */
|
|
339 private bool fIsAllowedToModifyDocument= true;
|
|
340
|
|
341
|
|
342 /** The text viewer's document. */
|
|
343 private IDocument fDocument;
|
|
344 /** The text viewer */
|
|
345 private ITextViewer fViewer;
|
|
346
|
|
347
|
|
348 /**
|
|
349 * Processes a dirty region. If the dirty region is <code>null</code> the whole
|
|
350 * document is consider being dirty. The dirty region is partitioned by the
|
|
351 * document and each partition is handed over to a reconciling strategy registered
|
|
352 * for the partition's content type.
|
|
353 *
|
|
354 * @param dirtyRegion the dirty region to be processed
|
|
355 */
|
|
356 abstract protected void process(DirtyRegion dirtyRegion);
|
|
357
|
|
358 /**
|
|
359 * Hook called when the document whose contents should be reconciled
|
|
360 * has been changed, i.e., the input document of the text viewer this
|
|
361 * reconciler is installed on. Usually, subclasses use this hook to
|
|
362 * inform all their reconciling strategies about the change.
|
|
363 *
|
|
364 * @param newDocument the new reconciler document
|
|
365 */
|
|
366 abstract protected void reconcilerDocumentChanged(IDocument newDocument);
|
|
367
|
|
368
|
|
369 /**
|
|
370 * Creates a new reconciler without configuring it.
|
|
371 */
|
|
372 protected AbstractReconciler() {
|
|
373 fProgressMonitor= new NullProgressMonitor();
|
|
374 }
|
|
375
|
|
376 /**
|
|
377 * Tells the reconciler how long it should wait for further text changes before
|
|
378 * activating the appropriate reconciling strategies.
|
|
379 *
|
|
380 * @param delay the duration in milliseconds of a change collection period.
|
|
381 */
|
|
382 public void setDelay(int delay) {
|
|
383 fDelay= delay;
|
|
384 }
|
|
385
|
|
386 /**
|
|
387 * Tells the reconciler whether any of the available reconciling strategies
|
|
388 * is interested in getting detailed dirty region information or just in the
|
|
389 * fact that the document has been changed. In the second case, the reconciling
|
|
390 * can not incrementally be pursued.
|
|
391 *
|
|
392 * @param isIncremental indicates whether this reconciler will be configured with
|
|
393 * incremental reconciling strategies
|
|
394 *
|
|
395 * @see DirtyRegion
|
|
396 * @see IReconcilingStrategy
|
|
397 */
|
|
398 public void setIsIncrementalReconciler(bool isIncremental) {
|
|
399 fIsIncrementalReconciler= isIncremental;
|
|
400 }
|
|
401
|
|
402 /**
|
|
403 * Tells the reconciler whether it is allowed to change the document
|
|
404 * inside its reconciler thread.
|
|
405 * <p>
|
|
406 * If this is set to <code>false</code> an {@link UnsupportedOperationException}
|
|
407 * will be thrown when this restriction will be violated.
|
|
408 * </p>
|
|
409 *
|
|
410 * @param isAllowedToModify indicates whether this reconciler is allowed to modify the document
|
|
411 * @since 3.2
|
|
412 */
|
|
413 public void setIsAllowedToModifyDocument(bool isAllowedToModify) {
|
|
414 fIsAllowedToModifyDocument= isAllowedToModify;
|
|
415 }
|
|
416
|
|
417 /**
|
|
418 * Sets the progress monitor of this reconciler.
|
|
419 *
|
|
420 * @param monitor the monitor to be used
|
|
421 */
|
|
422 public void setProgressMonitor(IProgressMonitor monitor) {
|
|
423 Assert.isLegal(monitor !is null);
|
|
424 fProgressMonitor= monitor;
|
|
425 }
|
|
426
|
|
427 /**
|
|
428 * Returns whether any of the reconciling strategies is interested in
|
|
429 * detailed dirty region information.
|
|
430 *
|
|
431 * @return whether this reconciler is incremental
|
|
432 *
|
|
433 * @see IReconcilingStrategy
|
|
434 */
|
|
435 protected bool isIncrementalReconciler() {
|
|
436 return fIsIncrementalReconciler;
|
|
437 }
|
|
438
|
|
439 /**
|
|
440 * Returns the input document of the text viewer this reconciler is installed on.
|
|
441 *
|
|
442 * @return the reconciler document
|
|
443 */
|
|
444 protected IDocument getDocument() {
|
|
445 return fDocument;
|
|
446 }
|
|
447
|
|
448 /**
|
|
449 * Returns the text viewer this reconciler is installed on.
|
|
450 *
|
|
451 * @return the text viewer this reconciler is installed on
|
|
452 */
|
|
453 protected ITextViewer getTextViewer() {
|
|
454 return fViewer;
|
|
455 }
|
|
456
|
|
457 /**
|
|
458 * Returns the progress monitor of this reconciler.
|
|
459 *
|
|
460 * @return the progress monitor of this reconciler
|
|
461 */
|
|
462 protected IProgressMonitor getProgressMonitor() {
|
|
463 return fProgressMonitor;
|
|
464 }
|
|
465
|
|
466 /*
|
|
467 * @see IReconciler#install(ITextViewer)
|
|
468 */
|
|
469 public void install(ITextViewer textViewer) {
|
|
470
|
|
471 Assert.isNotNull(textViewer);
|
|
472 fViewer= textViewer;
|
|
473
|
|
474 synchronized (this) {
|
|
475 if (fThread !is null)
|
|
476 return;
|
|
477 fThread= new BackgroundThread(getClass().getName());
|
|
478 }
|
|
479
|
|
480 fDirtyRegionQueue= new DirtyRegionQueue();
|
|
481
|
|
482 fListener= new Listener();
|
|
483 fViewer.addTextInputListener(fListener);
|
|
484
|
|
485 // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=67046
|
|
486 // if the reconciler gets installed on a viewer that already has a document
|
|
487 // (e.g. when reusing editors), we force the listener to register
|
|
488 // itself as document listener, because there will be no input change
|
|
489 // on the viewer.
|
|
490 // In order to do that, we simulate an input change.
|
|
491 IDocument document= textViewer.getDocument();
|
|
492 if (document !is null) {
|
|
493 fListener.inputDocumentAboutToBeChanged(fDocument, document);
|
|
494 fListener.inputDocumentChanged(fDocument, document);
|
|
495 }
|
|
496 }
|
|
497
|
|
498 /*
|
|
499 * @see IReconciler#uninstall()
|
|
500 */
|
|
501 public void uninstall() {
|
|
502 if (fListener !is null) {
|
|
503
|
|
504 fViewer.removeTextInputListener(fListener);
|
|
505 if (fDocument !is null) {
|
|
506 fListener.inputDocumentAboutToBeChanged(fDocument, null);
|
|
507 fListener.inputDocumentChanged(fDocument, null);
|
|
508 }
|
|
509 fListener= null;
|
|
510
|
|
511 synchronized (this) {
|
|
512 // http://dev.eclipse.org/bugs/show_bug.cgi?id=19135
|
|
513 BackgroundThread bt= fThread;
|
|
514 fThread= null;
|
|
515 bt.cancel();
|
|
516 }
|
|
517 }
|
|
518 }
|
|
519
|
|
520 /**
|
|
521 * Creates a dirty region for a document event and adds it to the queue.
|
|
522 *
|
|
523 * @param e the document event for which to create a dirty region
|
|
524 */
|
|
525 private void createDirtyRegion(DocumentEvent e) {
|
|
526 synchronized (fDirtyRegionQueue) {
|
|
527 if (e.getLength() is 0 && e.getText() !is null) {
|
|
528 // Insert
|
|
529 fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
|
|
530
|
|
531 } else if (e.getText() is null || e.getText().length() is 0) {
|
|
532 // Remove
|
|
533 fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
|
|
534
|
|
535 } else {
|
|
536 // Replace (Remove + Insert)
|
|
537 fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getLength(), DirtyRegion.REMOVE, null));
|
|
538 fDirtyRegionQueue.addDirtyRegion(new DirtyRegion(e.getOffset(), e.getText().length(), DirtyRegion.INSERT, e.getText()));
|
|
539 }
|
|
540 }
|
|
541 }
|
|
542
|
|
543 /**
|
|
544 * Hook for subclasses which want to perform some
|
|
545 * action as soon as reconciliation is needed.
|
|
546 * <p>
|
|
547 * Default implementation is to do nothing.
|
|
548 * </p>
|
|
549 *
|
|
550 * @since 3.0
|
|
551 */
|
|
552 protected void aboutToBeReconciled() {
|
|
553 }
|
|
554
|
|
555 /**
|
|
556 * This method is called on startup of the background activity. It is called only
|
|
557 * once during the life time of the reconciler. Clients may reimplement this method.
|
|
558 */
|
|
559 protected void initialProcess() {
|
|
560 }
|
|
561
|
|
562 /**
|
|
563 * Forces the reconciler to reconcile the structure of the whole document.
|
|
564 * Clients may extend this method.
|
|
565 */
|
|
566 protected void forceReconciling() {
|
|
567
|
|
568 if (fDocument !is null) {
|
|
569
|
|
570 if (!fThread.isDirty()&& fThread.isAlive())
|
|
571 aboutToBeReconciled();
|
|
572
|
|
573 if (fThread.isActive())
|
|
574 fProgressMonitor.setCanceled(true);
|
|
575
|
|
576 if (fIsIncrementalReconciler) {
|
|
577 DocumentEvent e= new DocumentEvent(fDocument, 0, fDocument.getLength(), fDocument.get());
|
|
578 createDirtyRegion(e);
|
|
579 }
|
|
580
|
|
581 startReconciling();
|
|
582 }
|
|
583 }
|
|
584
|
|
585 /**
|
|
586 * Starts the reconciler to reconcile the queued dirty-regions.
|
|
587 * Clients may extend this method.
|
|
588 */
|
|
589 protected synchronized void startReconciling() {
|
|
590 if (fThread is null)
|
|
591 return;
|
|
592
|
|
593 if (!fThread.isAlive()) {
|
|
594 try {
|
|
595 fThread.start();
|
|
596 } catch (IllegalThreadStateException e) {
|
|
597 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=40549
|
|
598 // This is the only instance where the thread is started; since
|
|
599 // we checked that it is not alive, it must be dead already due
|
|
600 // to a run-time exception or error. Exit.
|
|
601 }
|
|
602 } else {
|
|
603 fThread.reset();
|
|
604 }
|
|
605 }
|
|
606
|
|
607 /**
|
|
608 * Hook that is called after the reconciler thread has been reset.
|
|
609 */
|
|
610 protected void reconcilerReset() {
|
|
611 }
|
|
612
|
|
613 /**
|
|
614 * Tells whether the code is running in this reconciler's
|
|
615 * background thread.
|
|
616 *
|
|
617 * @return <code>true</code> if running in this reconciler's background thread
|
|
618 * @since 3.4
|
|
619 */
|
|
620 protected bool isRunningInReconcilerThread() {
|
|
621 return Thread.currentThread() is fThread;
|
|
622 }
|
|
623 }
|