add DumbService.filterByDumbAwareness(T[])
[idea/community.git] / platform / core-api / src / com / intellij / openapi / project / DumbService.java
1 /*
2  * Copyright 2000-2014 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.openapi.project;
17
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.application.ModalityState;
21 import com.intellij.openapi.components.ServiceManager;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.util.Computable;
24 import com.intellij.openapi.util.NotNullLazyKey;
25 import com.intellij.openapi.util.Ref;
26 import com.intellij.util.messages.Topic;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
29
30 import javax.swing.*;
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.Collection;
34 import java.util.List;
35
36 /**
37  * A service managing IDEA's 'dumb' mode: when indices are updated in background and the functionality is very much limited.
38  * Only the explicitly allowed functionality is available. Usually it's allowed by implementing {@link DumbAware} interface.
39  *
40  * If you want to register a toolwindow, which will be enabled during the dumb mode, please use {@link com.intellij.openapi.wm.ToolWindowManager}'s
41  * registration methods which have 'canWorkInDumMode' parameter. 
42  *
43  * @author peter
44  */
45 public abstract class DumbService {
46   private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.project.DumbService");
47
48   /**
49    * @see Project#getMessageBus()
50    */
51   public static final Topic<DumbModeListener> DUMB_MODE = new Topic<DumbModeListener>("dumb mode", DumbModeListener.class);
52
53   /**
54    * @return whether IntelliJ IDEA is in dumb mode, which means that right now indices are updated in background.
55    * IDEA offers only limited functionality at such times, e.g. plain text file editing and version control operations.
56    */
57   public abstract boolean isDumb();
58
59   public static boolean isDumb(@NotNull Project project) {
60     return getInstance(project).isDumb();
61   }
62
63   /**
64    * Executes the runnable immediately if not in dumb mode, or on AWT Event Dispatch thread when the dumb mode ends.
65    * @param runnable runnable to run
66    */
67   public abstract void runWhenSmart(@NotNull Runnable runnable);
68
69   /**
70    * Pause the current thread until dumb mode ends and then continue execution.
71    * NOTE: there are no guarantees that a new dumb mode won't begin before the next statement.
72    * Hence: use with care. Consider using {@link #runWhenSmart(Runnable)}, {@link #runReadActionInSmartMode(Runnable)} or {@link #repeatUntilPassesInSmartMode(Runnable)} instead
73    */
74   public abstract void waitForSmartMode();
75
76   /**
77    * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action.
78    */
79   public <T> T runReadActionInSmartMode(@NotNull final Computable<T> r) {
80     final Ref<T> result = new Ref<T>();
81     runReadActionInSmartMode(new Runnable() {
82       @Override
83       public void run() {
84         result.set(r.compute());
85       }
86     });
87     return result.get();
88   }
89
90   @Nullable
91   public <T> T tryRunReadActionInSmartMode(@NotNull Computable<T> task, @Nullable String notification) {
92     if (ApplicationManager.getApplication().isReadAccessAllowed()) {
93       try {
94         return task.compute();
95       }
96       catch (IndexNotReadyException e) {
97         if (notification != null) {
98           showDumbModeNotification(notification);
99         }
100         return null;
101       }
102     }
103     else {
104       return runReadActionInSmartMode(task);
105     }
106   }
107
108   /**
109    * Pause the current thread until dumb mode ends, and then run the read action. Index is guaranteed to be available inside that read action.
110    */
111   public void runReadActionInSmartMode(@NotNull final Runnable r) {
112     while (true) {
113       waitForSmartMode();
114       boolean success = ApplicationManager.getApplication().runReadAction(new Computable<Boolean>() {
115         @Override
116         public Boolean compute() {
117           if (isDumb()) {
118             return false;
119           }
120           r.run();
121           return true;
122         }
123       });
124       if (success) break;
125     }
126   }
127
128   /**
129    * Pause the current thread until dumb mode ends, and then attempt to execute the runnable. If it fails due to another dumb mode having started,
130    * try again until the runnable is able to complete successfully.
131    * It makes sense to use this method when you have a long-running activity consisting of many small read actions, and you don't want to
132    * use a single long read action in order to keep the IDE responsive.
133    * 
134    * @see #runReadActionInSmartMode(Runnable) 
135    */
136   public void repeatUntilPassesInSmartMode(@NotNull final Runnable r) {
137     while (true) {
138       waitForSmartMode();
139       try {
140         r.run();
141         return;
142       }
143       catch (IndexNotReadyException e) {
144         LOG.info(e);
145       }
146     }
147   }
148
149   /**
150    * Invoke the runnable later on EventDispatchThread AND when IDEA isn't in dumb mode
151    * @param runnable runnable
152    */
153   public abstract void smartInvokeLater(@NotNull Runnable runnable);
154
155   public abstract void smartInvokeLater(@NotNull Runnable runnable, @NotNull ModalityState modalityState);
156
157   private static final NotNullLazyKey<DumbService, Project> INSTANCE_KEY = ServiceManager.createLazyKey(DumbService.class);
158
159   public static DumbService getInstance(@NotNull Project project) {
160     return INSTANCE_KEY.getValue(project);
161   }
162
163   /**
164    * @return all the elements of the given array if there's no dumb mode currently, or the dumb-aware ones if {@link #isDumb()} is true.
165    * @see #isDumbAware(Object) 
166    */
167   @NotNull
168   public <T> List<T> filterByDumbAwareness(@NotNull T[] array) {
169     return filterByDumbAwareness(Arrays.asList(array));
170   }
171
172   /**
173    * @return all the elements of the given collection if there's no dumb mode currently, or the dumb-aware ones if {@link #isDumb()} is true. 
174    * @see #isDumbAware(Object)
175    */
176   @NotNull
177   public <T> List<T> filterByDumbAwareness(@NotNull Collection<T> collection) {
178     if (isDumb()) {
179       final ArrayList<T> result = new ArrayList<T>(collection.size());
180       for (T element : collection) {
181         if (isDumbAware(element)) {
182           result.add(element);
183         }
184       }
185       return result;
186     }
187
188     if (collection instanceof List) {
189       return (List<T>)collection;
190     }
191
192     return new ArrayList<T>(collection);
193   }
194
195   public abstract void queueTask(@NotNull DumbModeTask task);
196   
197   public abstract void cancelTask(@NotNull DumbModeTask task);
198
199   public abstract JComponent wrapGently(@NotNull JComponent dumbUnawareContent, @NotNull Disposable parentDisposable);
200
201   public void makeDumbAware(@NotNull final JComponent component, @NotNull Disposable disposable) {
202     component.setEnabled(!isDumb());
203     getProject().getMessageBus().connect(disposable).subscribe(DUMB_MODE, new DumbModeListener() {
204       @Override
205       public void enteredDumbMode() {
206         component.setEnabled(false);
207       }
208
209       @Override
210       public void exitDumbMode() {
211         component.setEnabled(true);
212       }
213     });
214   }
215
216   public abstract void showDumbModeNotification(@NotNull String message);
217
218   public abstract Project getProject();
219
220   public static boolean isDumbAware(Object o) {
221     if (o instanceof PossiblyDumbAware) {
222       return ((PossiblyDumbAware)o).isDumbAware();
223     }
224     return o instanceof DumbAware;
225   }
226
227   /**
228    * Enables or disables alternative resolve strategies for the current thread.<p/> 
229    * 
230    * Normally reference resolution uses index, and hence is not available in dumb mode. In some cases, alternative ways
231    * of performing resolve are available, although much slower. It's impractical to always use these ways because it'll
232    * lead to overloaded CPU (especially given there's also indexing in progress). But for some explicit user actions
233    * (e.g. explicit Goto Declaration) turning these slower methods is beneficial.<p/>
234    *
235    * NOTE: even with alternative resolution enabled, methods like resolve(), findClass() etc may still throw
236    * {@link IndexNotReadyException}. So alternative resolve is not a panacea, it might help provide navigation in some cases
237    * but not in all.<p/>
238    * 
239    * A typical usage would involve try-finally, where the alternative resolution is first enabled, then an action is performed,
240    * and then alternative resolution is turned off in the finally block.
241    */
242   public abstract void setAlternativeResolveEnabled(boolean enabled);
243
244   /**
245    * @return whether alternative resolution is enabled for the current thread.
246    * 
247    * @see #setAlternativeResolveEnabled(boolean) 
248    */
249   public abstract boolean isAlternativeResolveEnabled();
250
251   /**
252    * @see #DUMB_MODE
253    */
254   public interface DumbModeListener {
255
256     /**
257      * The event arrives on EDT
258      */
259     void enteredDumbMode();
260
261     /**
262      * The event arrives on EDT
263      */
264     void exitDumbMode();
265
266   }
267 }