/platform/platform-api/src/com/intellij/ide/util/treeView/AbstractTreeUpdater.java

https://bitbucket.org/nbargnesi/idea · Java · 439 lines · 328 code · 74 blank · 37 comment · 53 complexity · 9c1ddb5889ee4cbcd4c0f983e4d4852c MD5 · raw file

  1. /*
  2. * Copyright 2000-2012 JetBrains s.r.o.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package com.intellij.ide.util.treeView;
  17. import com.intellij.openapi.Disposable;
  18. import com.intellij.openapi.application.Application;
  19. import com.intellij.openapi.application.ApplicationManager;
  20. import com.intellij.openapi.application.ModalityState;
  21. import com.intellij.openapi.diagnostic.Logger;
  22. import com.intellij.openapi.progress.ProcessCanceledException;
  23. import com.intellij.openapi.util.ActionCallback;
  24. import com.intellij.openapi.util.Disposer;
  25. import com.intellij.ui.treeStructure.treetable.TreeTableTree;
  26. import com.intellij.util.Alarm;
  27. import com.intellij.util.ui.UIUtil;
  28. import com.intellij.util.ui.update.Activatable;
  29. import com.intellij.util.ui.update.MergingUpdateQueue;
  30. import com.intellij.util.ui.update.UiNotifyConnector;
  31. import com.intellij.util.ui.update.Update;
  32. import org.jetbrains.annotations.NonNls;
  33. import org.jetbrains.annotations.NotNull;
  34. import javax.swing.*;
  35. import javax.swing.tree.DefaultMutableTreeNode;
  36. import java.util.*;
  37. public class AbstractTreeUpdater implements Disposable, Activatable {
  38. private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.AbstractTreeUpdater");
  39. private final LinkedList<TreeUpdatePass> myNodeQueue = new LinkedList<TreeUpdatePass>();
  40. private final AbstractTreeBuilder myTreeBuilder;
  41. private final List<Runnable> myRunAfterUpdate = new ArrayList<Runnable>();
  42. private Runnable myRunBeforeUpdate;
  43. private final MergingUpdateQueue myUpdateQueue;
  44. private long myUpdateCount;
  45. private boolean myReleaseRequested;
  46. public AbstractTreeUpdater(@NotNull AbstractTreeBuilder treeBuilder) {
  47. myTreeBuilder = treeBuilder;
  48. final JTree tree = myTreeBuilder.getTree();
  49. final JComponent component = tree instanceof TreeTableTree ? ((TreeTableTree)tree).getTreeTable() : tree;
  50. myUpdateQueue = new MergingUpdateQueue("UpdateQueue", 300, component.isShowing(), component) {
  51. @Override
  52. protected Alarm createAlarm(@NotNull Alarm.ThreadToUse thread, Disposable parent) {
  53. return new Alarm(thread, parent) {
  54. @Override
  55. protected boolean isEdt() {
  56. return AbstractTreeUpdater.this.isEdt();
  57. }
  58. };
  59. }
  60. };
  61. myUpdateQueue.setRestartTimerOnAdd(false);
  62. final UiNotifyConnector uiNotifyConnector = new UiNotifyConnector(component, myUpdateQueue);
  63. Disposer.register(this, myUpdateQueue);
  64. Disposer.register(this, uiNotifyConnector);
  65. }
  66. /**
  67. * @param delay update delay in milliseconds.
  68. */
  69. public void setDelay(int delay) {
  70. myUpdateQueue.setMergingTimeSpan(delay);
  71. }
  72. public void setPassThroughMode(boolean passThroughMode) {
  73. myUpdateQueue.setPassThrough(passThroughMode);
  74. }
  75. public void setModalityStateComponent(JComponent c) {
  76. myUpdateQueue.setModalityStateComponent(c);
  77. }
  78. public ModalityState getModalityState() {
  79. return myUpdateQueue.getModalityState();
  80. }
  81. public boolean hasNodesToUpdate() {
  82. return !myNodeQueue.isEmpty() || !myUpdateQueue.isEmpty();
  83. }
  84. @Override
  85. public void dispose() {
  86. }
  87. /**
  88. * @deprecated use {@link com.intellij.ide.util.treeView.AbstractTreeBuilder#queueUpdateFrom(Object, boolean)}
  89. */
  90. public synchronized void addSubtreeToUpdate(@NotNull DefaultMutableTreeNode rootNode) {
  91. addSubtreeToUpdate(new TreeUpdatePass(rootNode).setUpdateStamp(-1));
  92. }
  93. /**
  94. * @deprecated use {@link com.intellij.ide.util.treeView.AbstractTreeBuilder#queueUpdateFrom(Object, boolean)}
  95. */
  96. public synchronized void requeue(@NotNull TreeUpdatePass toAdd) {
  97. addSubtreeToUpdate(toAdd.setUpdateStamp(-1));
  98. }
  99. /**
  100. * @deprecated use {@link com.intellij.ide.util.treeView.AbstractTreeBuilder#queueUpdateFrom(Object, boolean)}
  101. */
  102. public synchronized void addSubtreeToUpdate(@NotNull TreeUpdatePass toAdd) {
  103. if (myReleaseRequested) return;
  104. assert !toAdd.isExpired();
  105. final AbstractTreeUi ui = myTreeBuilder.getUi();
  106. if (ui.isUpdatingChildrenNow(toAdd.getNode())) {
  107. toAdd.expire();
  108. }
  109. else {
  110. for (Iterator<TreeUpdatePass> iterator = myNodeQueue.iterator(); iterator.hasNext();) {
  111. final TreeUpdatePass passInQueue = iterator.next();
  112. if (toAdd.isUpdateStructure() == passInQueue.isUpdateStructure()) {
  113. if (passInQueue == toAdd) {
  114. toAdd.expire();
  115. break;
  116. }
  117. if (passInQueue.getNode() == toAdd.getNode()) {
  118. toAdd.expire();
  119. break;
  120. }
  121. if (toAdd.getNode().isNodeAncestor(passInQueue.getNode())) {
  122. toAdd.expire();
  123. break;
  124. }
  125. if (passInQueue.getNode().isNodeAncestor(toAdd.getNode())) {
  126. iterator.remove();
  127. passInQueue.expire();
  128. }
  129. }
  130. }
  131. }
  132. if (toAdd.getUpdateStamp() >= 0) {
  133. Object element = ui.getElementFor(toAdd.getNode());
  134. if (!ui.isParentLoadingInBackground(element) && !ui.isParentUpdatingChildrenNow(toAdd.getNode())) {
  135. toAdd.setUpdateStamp(-1);
  136. }
  137. }
  138. long newUpdateCount = toAdd.getUpdateStamp() == -1 ? myUpdateCount : myUpdateCount + 1;
  139. if (!toAdd.isExpired()) {
  140. final Collection<TreeUpdatePass> yielding = ui.getYeildingPasses();
  141. for (TreeUpdatePass eachYielding : yielding) {
  142. final DefaultMutableTreeNode eachNode = eachYielding.getCurrentNode();
  143. if (eachNode != null) {
  144. if (eachNode.isNodeAncestor(toAdd.getNode())) {
  145. eachYielding.setUpdateStamp(newUpdateCount);
  146. }
  147. }
  148. }
  149. }
  150. if (toAdd.isExpired()) {
  151. reQueueViewUpdateIfNeeded();
  152. return;
  153. }
  154. myNodeQueue.add(toAdd);
  155. myTreeBuilder.getUi().addActivity();
  156. myUpdateCount = newUpdateCount;
  157. toAdd.setUpdateStamp(myUpdateCount);
  158. reQueueViewUpdate();
  159. }
  160. private void reQueueViewUpdateIfNeeded() {
  161. if (myUpdateQueue.isEmpty() && !myNodeQueue.isEmpty()) {
  162. reQueueViewUpdate();
  163. }
  164. }
  165. private void reQueueViewUpdate() {
  166. queue(new Update("ViewUpdate") {
  167. @Override
  168. public boolean isExpired() {
  169. return myTreeBuilder.isDisposed();
  170. }
  171. @Override
  172. public void run() {
  173. AbstractTreeStructure structure = myTreeBuilder.getTreeStructure();
  174. if (structure.hasSomethingToCommit()) {
  175. structure.asyncCommit().doWhenDone(new Runnable() {
  176. @Override
  177. public void run() {
  178. reQueueViewUpdateIfNeeded();
  179. }
  180. });
  181. return;
  182. }
  183. try {
  184. performUpdate();
  185. }
  186. catch (ProcessCanceledException e) {
  187. throw e;
  188. }
  189. catch (RuntimeException e) {
  190. LOG.error(myTreeBuilder.getClass().getName(), e);
  191. }
  192. }
  193. });
  194. }
  195. private void queue(@NotNull Update update) {
  196. if (isReleased()) return;
  197. myUpdateQueue.queue(update);
  198. }
  199. /**
  200. * @param node
  201. * @deprecated use addSubtreeToUpdate instead
  202. */
  203. protected void updateSubtree(DefaultMutableTreeNode node) {
  204. myTreeBuilder.updateSubtree(node);
  205. }
  206. public synchronized void performUpdate() {
  207. if (myRunBeforeUpdate != null) {
  208. myRunBeforeUpdate.run();
  209. myRunBeforeUpdate = null;
  210. }
  211. while (!myNodeQueue.isEmpty()) {
  212. if (isInPostponeMode()) break;
  213. final TreeUpdatePass eachPass = myNodeQueue.removeFirst();
  214. beforeUpdate(eachPass).doWhenDone(new Runnable() {
  215. @Override
  216. public void run() {
  217. try {
  218. myTreeBuilder.getUi().updateSubtreeNow(eachPass, false);
  219. }
  220. catch (ProcessCanceledException ignored) {
  221. }
  222. }
  223. });
  224. }
  225. if (isReleased()) return;
  226. myTreeBuilder.getUi().maybeReady();
  227. maybeRunAfterUpdate();
  228. }
  229. private void maybeRunAfterUpdate() {
  230. if (myRunAfterUpdate != null) {
  231. final Runnable runnable = new Runnable() {
  232. @Override
  233. public void run() {
  234. List<Runnable> runAfterUpdate = null;
  235. synchronized (myRunAfterUpdate) {
  236. if (!myRunAfterUpdate.isEmpty()) {
  237. runAfterUpdate = new ArrayList<Runnable>(myRunAfterUpdate);
  238. myRunAfterUpdate.clear();
  239. }
  240. }
  241. if (runAfterUpdate != null) {
  242. for (Runnable r : runAfterUpdate) {
  243. r.run();
  244. }
  245. }
  246. }
  247. };
  248. myTreeBuilder.getReady(this).doWhenDone(runnable);
  249. }
  250. }
  251. private boolean isReleased() {
  252. return myTreeBuilder.getUi() == null;
  253. }
  254. protected void invokeLater(Runnable runnable) {
  255. if (myTreeBuilder.getUi().isPassthroughMode()) {
  256. runnable.run();
  257. } else {
  258. final Application app = ApplicationManager.getApplication();
  259. if (app != null) {
  260. app.invokeLater(runnable);
  261. }
  262. else {
  263. UIUtil.invokeAndWaitIfNeeded(runnable);
  264. }
  265. }
  266. }
  267. protected ActionCallback beforeUpdate(TreeUpdatePass pass) {
  268. return new ActionCallback.Done();
  269. }
  270. /**
  271. * @deprecated use {@link com.intellij.ide.util.treeView.AbstractTreeBuilder#queueUpdateFrom(Object, boolean)}
  272. */
  273. public boolean addSubtreeToUpdateByElement(Object element) {
  274. return addSubtreeToUpdateByElement(element, false);
  275. }
  276. /**
  277. * @deprecated use {@link com.intellij.ide.util.treeView.AbstractTreeBuilder#queueUpdateFrom(Object, boolean)}
  278. */
  279. public boolean addSubtreeToUpdateByElement(Object element, boolean forceResort) {
  280. DefaultMutableTreeNode node = myTreeBuilder.getNodeForElement(element);
  281. if (node != null) {
  282. myTreeBuilder.queueUpdateFrom(element, forceResort);
  283. return true;
  284. } else {
  285. return false;
  286. }
  287. }
  288. public void cancelAllRequests() {
  289. myNodeQueue.clear();
  290. myUpdateQueue.cancelAllUpdates();
  291. }
  292. public void runAfterUpdate(final Runnable runnable) {
  293. if (runnable == null) return;
  294. synchronized (myRunAfterUpdate) {
  295. myRunAfterUpdate.add(runnable);
  296. }
  297. maybeRunAfterUpdate();
  298. }
  299. public synchronized void runBeforeUpdate(final Runnable runnable) {
  300. myRunBeforeUpdate = runnable;
  301. }
  302. public synchronized long getUpdateCount() {
  303. return myUpdateCount;
  304. }
  305. public boolean isRerunNeededFor(TreeUpdatePass pass) {
  306. return pass.getUpdateStamp() < getUpdateCount();
  307. }
  308. public boolean isInPostponeMode() {
  309. return !myUpdateQueue.isActive() && !myUpdateQueue.isPassThrough();
  310. }
  311. @Override
  312. public void showNotify() {
  313. myUpdateQueue.showNotify();
  314. }
  315. @Override
  316. public void hideNotify() {
  317. myUpdateQueue.hideNotify();
  318. }
  319. protected boolean isEdt() {
  320. return Alarm.isEventDispatchThread();
  321. }
  322. @NonNls
  323. @Override
  324. public synchronized String toString() {
  325. return "AbstractTreeUpdater updateCount=" + myUpdateCount + " queue=[" + myUpdateQueue.toString() + "] " + " nodeQueue=" + myNodeQueue;
  326. }
  327. public void flush() {
  328. myUpdateQueue.sendFlush();
  329. }
  330. public boolean isEnqueuedToUpdate(DefaultMutableTreeNode node) {
  331. for (TreeUpdatePass pass : myNodeQueue) {
  332. if (pass.willUpdate(node)) return true;
  333. }
  334. return false;
  335. }
  336. public final void queueSelection(final SelectionRequest request) {
  337. queue(new Update("UserSelection", Update.LOW_PRIORITY) {
  338. @Override
  339. public void run() {
  340. request.execute(myTreeBuilder.getUi());
  341. }
  342. @Override
  343. public boolean isExpired() {
  344. return myTreeBuilder.isDisposed();
  345. }
  346. @Override
  347. public void setRejected() {
  348. request.reject();
  349. }
  350. });
  351. }
  352. public synchronized void requestRelease() {
  353. myReleaseRequested = true;
  354. reset();
  355. myUpdateQueue.deactivate();
  356. }
  357. public void reset() {
  358. TreeUpdatePass[] passes = myNodeQueue.toArray(new TreeUpdatePass[myNodeQueue.size()]);
  359. myNodeQueue.clear();
  360. myUpdateQueue.cancelAllUpdates();
  361. for (TreeUpdatePass each : passes) {
  362. myTreeBuilder.getUi().addToCancelled(each.getNode());
  363. }
  364. }
  365. }