1 package com
.intellij
.openapi
.roots
.ui
.configuration
.projectRoot
.daemon
;
3 import com
.intellij
.openapi
.Disposable
;
4 import com
.intellij
.openapi
.application
.ReadAction
;
5 import com
.intellij
.openapi
.application
.Result
;
6 import com
.intellij
.openapi
.diagnostic
.Logger
;
7 import com
.intellij
.openapi
.roots
.ui
.configuration
.projectRoot
.StructureConfigurableContext
;
8 import com
.intellij
.openapi
.util
.Disposer
;
9 import com
.intellij
.openapi
.util
.MultiValuesMap
;
10 import com
.intellij
.util
.EventDispatcher
;
11 import com
.intellij
.util
.ui
.update
.MergingUpdateQueue
;
12 import com
.intellij
.util
.ui
.update
.Update
;
13 import org
.jetbrains
.annotations
.NotNull
;
14 import org
.jetbrains
.annotations
.Nullable
;
18 import java
.util
.concurrent
.atomic
.AtomicBoolean
;
23 public class ProjectStructureDaemonAnalyzer
implements Disposable
{
24 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
25 private Map
<ProjectStructureElement
, ProjectStructureProblemsHolderImpl
> myProblemHolders
= new HashMap
<ProjectStructureElement
, ProjectStructureProblemsHolderImpl
>();
26 private MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
> mySourceElement2Usages
= new MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
>();
27 private MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
> myContainingElement2Usages
= new MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
>();
28 private Set
<ProjectStructureElement
> myElementWithNotCalculatedUsages
= new HashSet
<ProjectStructureElement
>();
29 private MergingUpdateQueue myAnalyzerQueue
;
30 private final EventDispatcher
<ProjectStructureDaemonAnalyzerListener
> myDispatcher
= EventDispatcher
.create(ProjectStructureDaemonAnalyzerListener
.class);
31 private final AtomicBoolean myStopped
= new AtomicBoolean(false);
33 public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context
) {
34 Disposer
.register(context
, this);
35 myAnalyzerQueue
= new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
38 private void doUpdate(final ProjectStructureElement element
, final boolean check
, final boolean collectUsages
) {
39 if (myStopped
.get()) return;
45 doCollectUsages(element
);
49 private void doCheck(final ProjectStructureElement element
) {
50 final ProjectStructureProblemsHolderImpl problemsHolder
= new ProjectStructureProblemsHolderImpl();
52 protected void run(final Result result
) {
53 if (myStopped
.get()) return;
55 if (LOG
.isDebugEnabled()) {
56 LOG
.debug("checking " + element
);
58 element
.check(problemsHolder
);
61 invokeLater(new Runnable() {
63 if (myStopped
.get()) return;
65 if (LOG
.isDebugEnabled()) {
66 LOG
.debug("updating problems for " + element
);
68 myProblemHolders
.put(element
, problemsHolder
);
69 myDispatcher
.getMulticaster().problemsChanged(element
);
74 private void doCollectUsages(final ProjectStructureElement element
) {
75 final List
<ProjectStructureElementUsage
> usages
= new ReadAction
<List
<ProjectStructureElementUsage
>>() {
76 protected void run(final Result
<List
<ProjectStructureElementUsage
>> result
) {
77 if (myStopped
.get()) return;
79 if (LOG
.isDebugEnabled()) {
80 LOG
.debug("collecting usages in " + element
);
82 result
.setResult(element
.getUsagesInElement());
84 }.execute().getResultObject();
86 invokeLater(new Runnable() {
88 if (myStopped
.get()) return;
90 if (LOG
.isDebugEnabled()) {
91 LOG
.debug("updating usages for " + element
);
93 updateUsages(element
, usages
);
94 myDispatcher
.getMulticaster().usagesCollected(element
);
99 private void updateUsages(ProjectStructureElement element
, List
<ProjectStructureElementUsage
> usages
) {
100 removeUsagesInElement(element
);
101 for (ProjectStructureElementUsage usage
: usages
) {
104 myElementWithNotCalculatedUsages
.remove(element
);
107 private static void invokeLater(Runnable runnable
) {
108 SwingUtilities
.invokeLater(runnable
);
111 public void queueUpdate(@NotNull final ProjectStructureElement element
) {
112 queueUpdate(element
, true, true);
115 public void queueUpdate(@NotNull final ProjectStructureElement element
, final boolean check
, final boolean collectUsages
) {
116 if (LOG
.isDebugEnabled()) {
117 LOG
.debug("start " + (check ?
"checking " : "") + (collectUsages ?
"collecting usages " : "") + "for " + element
);
120 myElementWithNotCalculatedUsages
.add(element
);
122 myAnalyzerQueue
.queue(new AnalyzeElementUpdate(element
, check
, collectUsages
));
125 public void removeElement(ProjectStructureElement element
) {
126 myElementWithNotCalculatedUsages
.remove(element
);
127 myProblemHolders
.remove(element
);
128 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.removeAll(element
);
129 if (usages
!= null) {
130 for (ProjectStructureElementUsage usage
: usages
) {
131 myProblemHolders
.remove(usage
.getContainingElement());
134 removeUsagesInElement(element
);
135 myDispatcher
.getMulticaster().problemsChanged(element
);
138 public boolean isUnused(ProjectStructureElement element
) {
139 if (!element
.highlightIfUnused()) {
142 if (!myElementWithNotCalculatedUsages
.isEmpty()) {
145 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.get(element
);
146 return usages
== null || usages
.isEmpty();
149 private void removeUsagesInElement(ProjectStructureElement element
) {
150 final Collection
<ProjectStructureElementUsage
> usages
= myContainingElement2Usages
.removeAll(element
);
151 if (usages
!= null) {
152 for (ProjectStructureElementUsage usage
: usages
) {
153 mySourceElement2Usages
.remove(usage
.getSourceElement(), usage
);
158 private void addUsage(@NotNull ProjectStructureElementUsage usage
) {
159 mySourceElement2Usages
.put(usage
.getSourceElement(), usage
);
160 myContainingElement2Usages
.put(usage
.getContainingElement(), usage
);
164 LOG
.debug("analyzer stopped");
166 myAnalyzerQueue
.cancelAllUpdates();
168 myAnalyzerQueue
.deactivate();
171 public void clearCaches() {
172 LOG
.debug("clear caches");
173 myProblemHolders
.clear();
176 public void clearAllProblems() {
177 myProblemHolders
.clear();
178 myDispatcher
.getMulticaster().allProblemsChanged();
181 public void dispose() {
183 myAnalyzerQueue
.cancelAllUpdates();
187 public ProjectStructureProblemsHolderImpl
getProblemsHolder(ProjectStructureElement element
) {
188 return myProblemHolders
.get(element
);
191 public Collection
<ProjectStructureElementUsage
> getUsages(ProjectStructureElement selected
) {
192 ProjectStructureElement
[] elements
= myElementWithNotCalculatedUsages
.toArray(new ProjectStructureElement
[myElementWithNotCalculatedUsages
.size()]);
193 for (ProjectStructureElement element
: elements
) {
194 updateUsages(element
, element
.getUsagesInElement());
196 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.get(selected
);
197 return usages
!= null ? usages
: Collections
.<ProjectStructureElementUsage
>emptyList();
200 public void addListener(ProjectStructureDaemonAnalyzerListener listener
) {
201 LOG
.debug("listener added " + listener
);
202 myDispatcher
.addListener(listener
);
205 public void reset() {
206 LOG
.debug("analyzer started");
207 myAnalyzerQueue
.activate();
208 myAnalyzerQueue
.queue(new Update("reset") {
210 myStopped
.set(false);
215 public void clear() {
216 mySourceElement2Usages
.clear();
217 myContainingElement2Usages
.clear();
218 myElementWithNotCalculatedUsages
.clear();
221 private class AnalyzeElementUpdate
extends Update
{
222 private final ProjectStructureElement myElement
;
223 private final boolean myCheck
;
224 private final boolean myCollectUsages
;
225 private final Object
[] myEqualityObjects
;
227 public AnalyzeElementUpdate(ProjectStructureElement element
, boolean check
, boolean collectUsages
) {
231 myCollectUsages
= collectUsages
;
232 myEqualityObjects
= new Object
[]{myElement
, myCheck
, myCollectUsages
};
236 public boolean canEat(Update update
) {
237 if (!(update
instanceof AnalyzeElementUpdate
)) return false;
238 final AnalyzeElementUpdate other
= (AnalyzeElementUpdate
)update
;
239 return myElement
.equals(other
.myElement
) && (!other
.myCheck
|| myCheck
) && (!other
.myCollectUsages
|| myCollectUsages
);
243 public Object
[] getEqualityObjects() {
244 return myEqualityObjects
;
248 doUpdate(myElement
, myCheck
, myCollectUsages
);