From 767cf7589b42e764957fa864dd28f4ec08a5a790 Mon Sep 17 00:00:00 2001 From: Kirill Kalishev Date: Tue, 15 Nov 2011 20:39:36 +0300 Subject: [PATCH] funcTests + macro playbacl: selected ui activities watching - 2 --- .../src/com/intellij/ide/UiActivity.java | 5 + .../intellij/ide/UiActivityMonitorImpl.java | 250 ++++++++++++++---- .../com/intellij/ide/ActivityMonitorTest.java | 37 ++- 3 files changed, 235 insertions(+), 57 deletions(-) diff --git a/platform/platform-api/src/com/intellij/ide/UiActivity.java b/platform/platform-api/src/com/intellij/ide/UiActivity.java index 1964b90a8f72..baf0d0ce9105 100644 --- a/platform/platform-api/src/com/intellij/ide/UiActivity.java +++ b/platform/platform-api/src/com/intellij/ide/UiActivity.java @@ -43,6 +43,11 @@ public class UiActivity implements ComparableObject{ return new Object[] {myElements}; } + @Override + public String toString() { + return "UiActivity: " + myElements; + } + @Override public int hashCode() { return ComparableObjectCheck.hashCode(this, super.hashCode()); diff --git a/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java b/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java index fd20b6845f25..93509357b87f 100644 --- a/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/UiActivityMonitorImpl.java @@ -31,16 +31,29 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; +import java.util.*; public class UiActivityMonitorImpl extends UiActivityMonitor implements ModalityStateListener, Disposable { - private final Map myObjects = new HashMap(); + private final Map myObjects = new HashMap(); public UiActivityMonitorImpl(Application application) { - myObjects.put(null, new BusyObjectGlobalImpl()); + myObjects.put(null, new BusyContainer() { + @Override + protected BusyImpl createBusyImpl(HashSet key) { + return new BusyImpl(key, this) { + @Override + public boolean isReady() { + for (Map.Entry entry : myObjects.entrySet()) { + final BusyContainer eachContainer = entry.getValue(); + final BusyImpl busy = eachContainer.getOrCreateBusy(myToWatchArray); + if (busy == this) continue; + if (!busy.isOwnReady()) return false; + } + return isOwnReady(); + } + }; + } + }); application.getMessageBus().connect().subscribe(ProjectLifecycleListener.TOPIC, new ProjectLifecycleListener() { @Override public void projectComponentsInitialized(Project project) { @@ -85,19 +98,20 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } public void maybeReady() { - for (Map.Entry entry : myObjects.entrySet()) { - entry.getValue().onReady(); + final Collection values = myObjects.values(); + for (BusyContainer each : values) { + each.onReady(); } } @Override - public BusyObject getBusy(@NotNull Project project, UiActivity ... toWatch) { - return _getBusy(project); + public BusyObject getBusy(@NotNull Project project, UiActivity... toWatch) { + return _getBusy(project, toWatch); } @Override - public BusyObject getBusy(UiActivity ... toWatch) { - return _getBusy(null); + public BusyObject getBusy(UiActivity... toWatch) { + return _getBusy(null, toWatch); } @Override @@ -106,7 +120,9 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } @Override - public void addActivity(@NotNull final Project project, @NotNull final UiActivity activity, @NotNull final ModalityState effectiveModalityState) { + public void addActivity(@NotNull final Project project, + @NotNull final UiActivity activity, + @NotNull final ModalityState effectiveModalityState) { invokeLaterIfNeeded(new MyRunnable() { @Override public void run(Throwable allocation) { @@ -120,7 +136,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } } - _getBusy(project).addActivity(activity, allocation, effectiveModalityState); + getBusyContainer(project).addActivity(activity, allocation, effectiveModalityState); } }); } @@ -149,7 +165,7 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality invokeLaterIfNeeded(new MyRunnable() { @Override public void run(Throwable allocation) { - _getBusy(null).addActivity(activity, allocation, effectiveModalityState); + getBusyContainer(null).addActivity(activity, allocation, effectiveModalityState); } }); } @@ -164,13 +180,17 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality }); } - private BusyImpl _getBusy(@Nullable Object key) { - BusyImpl object = myObjects.get(key); - return object != null ? object : getGlobalBusy(); + private BusyImpl _getBusy(@Nullable Object key, UiActivity... toWatch) { + return getBusyContainer(key).getOrCreateBusy(toWatch); + } + + private BusyContainer getBusyContainer(@Nullable Object key) { + BusyContainer container = myObjects.get(key); + return container != null ? container : getGlobalBusy(); } void initBusyObjectFor(@Nullable Object key) { - BusyImpl object = new BusyImpl(); + BusyContainer object = new BusyContainer(); myObjects.put(key, object); } @@ -178,18 +198,19 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality return myObjects.containsKey(project); } - private BusyObjectGlobalImpl getGlobalBusy() { - return (BusyObjectGlobalImpl)myObjects.get(null); + private BusyContainer getGlobalBusy() { + return myObjects.get(null); } public void clear() { - for (Map.Entry entry : myObjects.entrySet()) { - entry.getValue().clear(); + final Set keys = myObjects.keySet(); + for (Iterator iterator = keys.iterator(); iterator.hasNext(); ) { + Object each = iterator.next(); + myObjects.get(each).clear(); } } - @Override public void disposeComponent() { myObjects.clear(); @@ -227,20 +248,49 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality private class BusyImpl extends BusyObject.Impl { - private final Map myActivities = new HashMap(); + private final Map myActivities = new HashMap(); - private final Set myQueuedToRemove = new HashSet(); + private final Set myQueuedToRemove = new HashSet(); + + protected final Set myToWatch; + protected final UiActivity[] myToWatchArray; + private UiActivityMonitorImpl.BusyContainer myContainer; + + private BusyImpl(Set toWatch, BusyContainer container) { + myToWatch = toWatch; + myToWatchArray = toWatch.toArray(new UiActivity[toWatch.size()]); + myContainer = container; + } @Override public boolean isReady() { - return isOwnReady() && getGlobalBusy().isOwnReady(); + return isOwnReady() && getGlobalBusy().getOrCreateBusy(myToWatchArray).isOwnReady(); } boolean isOwnReady() { - if (myActivities.isEmpty()) return true; + Map infoToCheck = new HashMap(); + + final Iterator> activitySets = myContainer.myActivities2Object.keySet().iterator(); + while (activitySets.hasNext()) { + Set eachActivitySet = activitySets.next(); + final BusyImpl eachBusyObject = myContainer.myActivities2Object.get(eachActivitySet); + if (eachBusyObject == this) continue; + + for (UiActivity eachOtherActivity : eachActivitySet) { + for (UiActivity eachToWatch : myToWatch) { + if (eachToWatch.isSameOrGeneralFor(eachOtherActivity) && eachBusyObject.myActivities.containsKey(eachOtherActivity)) { + infoToCheck.put(eachOtherActivity, eachBusyObject.myActivities.get(eachOtherActivity)); + } + } + } + } + + infoToCheck.putAll(myActivities); + + if (infoToCheck.isEmpty()) return true; final ModalityState current = getCurrentState(); - for (Map.Entry entry : myActivities.entrySet()) { + for (Map.Entry entry : infoToCheck.entrySet()) { final ActivityInfo info = entry.getValue(); if (!current.dominates(info.getEffectiveState())) { return false; @@ -251,12 +301,18 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } - public void addActivity(Object activity, Throwable allocation, ModalityState effectiveModalityState) { + public void addActivity(UiActivity activity, Throwable allocation, ModalityState effectiveModalityState) { + if (!myToWatch.isEmpty()) { + if (!myToWatch.contains(activity)) return; + } + myActivities.put(activity, new ActivityInfo(allocation, effectiveModalityState)); myQueuedToRemove.remove(activity); + + myContainer.onActivityAdded(this, activity); } - public void removeActivity(final Object activity) { + public void removeActivity(final UiActivity activity) { if (!myActivities.containsKey(activity)) return; myQueuedToRemove.add(activity); @@ -268,44 +324,34 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality myQueuedToRemove.remove(activity); myActivities.remove(activity); + myContainer.onActivityRemoved(BusyImpl.this, activity); + onReady(); } }; if (isUnitTestMode()) { runnable.run(); - } else { + } + else { SwingUtilities.invokeLater(runnable); } } public void clear() { - Object[] activities = myActivities.keySet().toArray(new Object[myActivities.size()]); - for (Object each : activities) { + UiActivity[] activities = myActivities.keySet().toArray(new UiActivity[myActivities.size()]); + for (UiActivity each : activities) { removeActivity(each); } } } - private class BusyObjectGlobalImpl extends BusyImpl { - - @Override - public boolean isReady() { - for (Map.Entry entry : myObjects.entrySet()) { - BusyImpl busy = entry.getValue(); - if (busy == this) continue; - if (!busy.isOwnReady()) return false; - } - - return isOwnReady(); - } - } - private static void invokeLaterIfNeeded(final MyRunnable runnable) { final Throwable allocation = Registry.is("ide.debugMode") ? new Exception() : null; if (isUnitTestMode()) { runnable.run(allocation); - } else { + } + else { UIUtil.invokeLaterIfNeeded(new Runnable() { @Override public void run() { @@ -320,8 +366,110 @@ public class UiActivityMonitorImpl extends UiActivityMonitor implements Modality } private static boolean isUnitTestMode() { - Application app = ApplicationManager.getApplication(); - return app == null || app.isUnitTestMode(); + Application app = ApplicationManager.getApplication(); + return app == null || app.isUnitTestMode(); } + public class BusyContainer { + + private Map, BusyImpl> myActivities2Object = new HashMap, BusyImpl>(); + private Map> myObject2Activities = new HashMap>(); + + private Set myActivities = new HashSet(); + + private BusyImpl myDefault; + + private boolean myRemovingActivityNow; + + public BusyContainer() { + myDefault = registerBusyObject(new HashSet()); + } + + public BusyImpl getOrCreateBusy(UiActivity... activities) { + final HashSet key = new HashSet(); + key.addAll(Arrays.asList(activities)); + + if (myActivities2Object.containsKey(key)) { + return myActivities2Object.get(key); + } + else { + final BusyImpl busy = registerBusyObject(key); + return busy; + } + } + + private BusyImpl registerBusyObject(HashSet key) { + final BusyImpl busy = createBusyImpl(key); + myActivities2Object.put(key, busy); + myObject2Activities.put(busy, key); + return busy; + } + + protected BusyImpl createBusyImpl(HashSet key) { + return new BusyImpl(key, this); + } + + public void onReady() { + final Iterator> keyIterator = myActivities2Object.keySet().iterator(); + while (keyIterator.hasNext()) { + Set eachKey = keyIterator.next(); + final BusyImpl busy = myActivities2Object.get(eachKey); + busy.onReady(); + if (busy.isReady()) { + keyIterator.remove(); + myObject2Activities.remove(busy); + } + } + } + + public void clear() { + final UiActivity[] activities = (UiActivity[])myActivities.toArray(); + for (UiActivity each : activities) { + removeActivity(each); + } + } + + public void onActivityAdded(BusyImpl busy, UiActivity activity) { + myActivities.add(activity); + } + + public void onActivityRemoved(BusyImpl busy, UiActivity activity) { + if (myRemovingActivityNow) return; + + final Map> toRemove = new HashMap>(); + + try { + myRemovingActivityNow = true; + + myActivities.remove(activity); + final Iterator objects = myObject2Activities.keySet().iterator(); + while (objects.hasNext()) { + BusyImpl each = objects.next(); + if (each != busy) { + each.removeActivity(activity); + } + if (each.isReady()) { + final Set activities = myObject2Activities.get(busy); + toRemove.put(busy, activities); + } + } + } + finally { + for (BusyImpl each : toRemove.keySet()) { + final Set activities = myObject2Activities.remove(each); + myActivities2Object.remove(activities); + } + + myRemovingActivityNow = false; + } + } + + public void addActivity(UiActivity activity, Throwable allocation, ModalityState state) { + getOrCreateBusy(activity); + final Set busies = myObject2Activities.keySet(); + for (BusyImpl each : busies) { + each.addActivity(activity, allocation, state); + } + } + } } diff --git a/platform/platform-impl/testSrc/com/intellij/ide/ActivityMonitorTest.java b/platform/platform-impl/testSrc/com/intellij/ide/ActivityMonitorTest.java index dcf08e552810..afe6bda57aed 100644 --- a/platform/platform-impl/testSrc/com/intellij/ide/ActivityMonitorTest.java +++ b/platform/platform-impl/testSrc/com/intellij/ide/ActivityMonitorTest.java @@ -117,7 +117,32 @@ public class ActivityMonitorTest extends UsefulTestCase { } public void testReadyWithWatchActivities() throws Exception { + final UiActivity root = new UiActivity("root"); + final UiActivity op1 = new UiActivity("root", "operation1"); + final UiActivity op2 = new UiActivity("root", "operation2"); + + final UiActivity op12 = new UiActivity("root", "operation1", "operation12"); + final UiActivity op121 = new UiActivity("root", "operation1", "operation12", "operation121"); + + + myMonitor.addActivity(op1); + assertBusy(null); + assertReady(null, op2); + assertBusy(null, op1); + assertBusy(null, root); + + myMonitor.removeActivity(op1); + assertReady(null); + assertReady(null, op2); + assertReady(null, op1); + assertReady(null, root); + + myMonitor.addActivity(op12); + assertBusy(null); + assertBusy(null, root); + assertBusy(null, op12); + assertReady(null, op121); } public void testModalityState() { @@ -159,9 +184,9 @@ public class ActivityMonitorTest extends UsefulTestCase { assertFalse(new UiActivity("root", "folder2").isSameOrGeneralFor(new UiActivity("anotherRoot"))); } - private void assertReady(@Nullable Project key) { - BusyObject.Impl busy = (BusyObject.Impl)(key != null ? myMonitor.getBusy(key) : myMonitor.getBusy()); - assertTrue(busy.isReady()); + private void assertReady(@Nullable Project key, UiActivity ... activities) { + BusyObject.Impl busy = (BusyObject.Impl)(key != null ? myMonitor.getBusy(key, activities) : myMonitor.getBusy(activities)); + assertTrue("Must be READY, but was: BUSY", busy.isReady()); final boolean[] done = new boolean[] {false}; busy.getReady(this).doWhenDone(new Runnable() { @@ -174,9 +199,9 @@ public class ActivityMonitorTest extends UsefulTestCase { assertTrue(done[0]); } - private void assertBusy(@Nullable Project key) { - BusyObject.Impl busy = (BusyObject.Impl)(key != null ? myMonitor.getBusy(key) : myMonitor.getBusy()); - assertFalse(busy.isReady()); + private void assertBusy(@Nullable Project key, UiActivity ... activities) { + BusyObject.Impl busy = (BusyObject.Impl)(key != null ? myMonitor.getBusy(key, activities) : myMonitor.getBusy(activities)); + assertFalse("Must be BUSY, but was: READY", busy.isReady()); } }