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
.ui
.update
.MergingUpdateQueue
;
11 import com
.intellij
.util
.ui
.update
.Update
;
12 import org
.jetbrains
.annotations
.NotNull
;
20 public class ProjectStructureDaemonAnalyzer
implements Disposable
{
21 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.roots.ui.configuration.projectRoot.validation.ProjectStructureDaemonAnalyzer");
22 private Map
<ProjectStructureElement
, ProjectStructureProblemsHolder
> myProblemHolders
= new HashMap
<ProjectStructureElement
, ProjectStructureProblemsHolder
>();
23 private MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
> mySourceElement2Usages
= new MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
>();
24 private MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
> myContainingElement2Usages
= new MultiValuesMap
<ProjectStructureElement
, ProjectStructureElementUsage
>();
25 private Set
<ProjectStructureElement
> myElementWithNotCalculatedUsages
= new HashSet
<ProjectStructureElement
>();
26 private MergingUpdateQueue myAnalyzerQueue
;
27 private List
<Runnable
> myListeners
= new ArrayList
<Runnable
>();
28 private boolean myDisposed
;
30 public ProjectStructureDaemonAnalyzer(StructureConfigurableContext context
) {
31 Disposer
.register(context
, this);
32 myAnalyzerQueue
= new MergingUpdateQueue("Project Structure Daemon Analyzer", 300, false, null, this, null, false);
35 private void doUpdate(final ProjectStructureElement element
, final boolean check
, final boolean collectUsages
) {
36 if (myDisposed
) return;
42 doCollectUsages(element
);
46 private void doCheck(final ProjectStructureElement element
) {
47 final ProjectStructureProblemsHolder problemsHolder
= new ProjectStructureProblemsHolder();
49 protected void run(final Result result
) {
50 if (LOG
.isDebugEnabled()) {
51 LOG
.debug("checking " + element
);
53 element
.check(problemsHolder
);
56 invokeLater(new Runnable() {
58 if (LOG
.isDebugEnabled()) {
59 LOG
.debug("updating problems for " + element
);
61 myProblemHolders
.put(element
, problemsHolder
);
62 fireProblemsUpdated();
67 private void fireProblemsUpdated() {
68 Runnable
[] listeners
= myListeners
.toArray(new Runnable
[myListeners
.size()]);
69 for (Runnable listener
: listeners
) {
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 (LOG
.isDebugEnabled()) {
78 LOG
.debug("collecting usages in " + element
);
80 result
.setResult(element
.getUsagesInElement());
82 }.execute().getResultObject();
84 invokeLater(new Runnable() {
86 if (LOG
.isDebugEnabled()) {
87 LOG
.debug("updating usages for " + element
);
89 updateUsages(element
, usages
);
90 fireProblemsUpdated();
95 private void updateUsages(ProjectStructureElement element
, List
<ProjectStructureElementUsage
> usages
) {
96 removeUsagesInElement(element
);
97 for (ProjectStructureElementUsage usage
: usages
) {
100 myElementWithNotCalculatedUsages
.remove(element
);
103 private static void invokeLater(Runnable runnable
) {
104 SwingUtilities
.invokeLater(runnable
);
107 public void queueUpdate(@NotNull final ProjectStructureElement element
) {
108 queueUpdate(element
, true, true);
111 public void queueUpdate(@NotNull final ProjectStructureElement element
, final boolean check
, final boolean collectUsages
) {
112 if (LOG
.isDebugEnabled()) {
113 LOG
.debug("start " + (check ?
"checking " : "") + (collectUsages ?
"collecting usages " : "") + "for " + element
);
116 myElementWithNotCalculatedUsages
.add(element
);
118 myAnalyzerQueue
.queue(new AnalyzeElementUpdate(element
, check
, collectUsages
));
121 public void removeElement(ProjectStructureElement element
) {
122 myElementWithNotCalculatedUsages
.remove(element
);
123 myProblemHolders
.remove(element
);
124 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.removeAll(element
);
125 if (usages
!= null) {
126 for (ProjectStructureElementUsage usage
: usages
) {
127 myProblemHolders
.remove(usage
.getContainingElement());
130 removeUsagesInElement(element
);
131 fireProblemsUpdated();
134 public boolean isUnused(ProjectStructureElement element
) {
135 if (!element
.highlightIfUnused()) {
138 if (!myElementWithNotCalculatedUsages
.isEmpty()) {
141 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.get(element
);
142 return usages
== null || usages
.isEmpty();
145 private void removeUsagesInElement(ProjectStructureElement element
) {
146 final Collection
<ProjectStructureElementUsage
> usages
= myContainingElement2Usages
.removeAll(element
);
147 if (usages
!= null) {
148 for (ProjectStructureElementUsage usage
: usages
) {
149 mySourceElement2Usages
.remove(usage
.getSourceElement(), usage
);
154 private void addUsage(@NotNull ProjectStructureElementUsage usage
) {
155 mySourceElement2Usages
.put(usage
.getSourceElement(), usage
);
156 myContainingElement2Usages
.put(usage
.getContainingElement(), usage
);
160 LOG
.debug("analyzer stopped");
161 myAnalyzerQueue
.cancelAllUpdates();
163 myAnalyzerQueue
.deactivate();
166 public void clearCaches() {
167 LOG
.debug("clear caches");
168 myProblemHolders
.clear();
169 mySourceElement2Usages
.clear();
170 myContainingElement2Usages
.clear();
171 myElementWithNotCalculatedUsages
.clear();
174 public void clearAllProblems() {
175 myProblemHolders
.clear();
176 fireProblemsUpdated();
179 public void dispose() {
181 myAnalyzerQueue
.cancelAllUpdates();
184 public ProjectStructureProblemsHolder
getProblemsHolder(ProjectStructureElement element
) {
185 return myProblemHolders
.get(element
);
188 public Collection
<ProjectStructureElementUsage
> getUsages(ProjectStructureElement selected
) {
189 ProjectStructureElement
[] elements
= myElementWithNotCalculatedUsages
.toArray(new ProjectStructureElement
[myElementWithNotCalculatedUsages
.size()]);
190 for (ProjectStructureElement element
: elements
) {
191 updateUsages(element
, element
.getUsagesInElement());
193 fireProblemsUpdated();
194 final Collection
<ProjectStructureElementUsage
> usages
= mySourceElement2Usages
.get(selected
);
195 return usages
!= null ? usages
: Collections
.<ProjectStructureElementUsage
>emptyList();
198 public void addListener(Runnable runnable
) {
199 LOG
.debug("listener added " + runnable
);
200 myListeners
.add(runnable
);
203 public void reset() {
204 LOG
.debug("analyzer started");
205 myAnalyzerQueue
.activate();
206 myAnalyzerQueue
.queue(new Update("reset") {
213 private class AnalyzeElementUpdate
extends Update
{
214 private final ProjectStructureElement myElement
;
215 private final boolean myCheck
;
216 private final boolean myCollectUsages
;
217 private final Object
[] myEqualityObjects
;
219 public AnalyzeElementUpdate(ProjectStructureElement element
, boolean check
, boolean collectUsages
) {
223 myCollectUsages
= collectUsages
;
224 myEqualityObjects
= new Object
[]{myElement
, myCheck
, myCollectUsages
};
228 public boolean canEat(Update update
) {
229 if (!(update
instanceof AnalyzeElementUpdate
)) return false;
230 final AnalyzeElementUpdate other
= (AnalyzeElementUpdate
)update
;
231 return myElement
.equals(other
.myElement
) && (!other
.myCheck
|| myCheck
) && (!other
.myCollectUsages
|| myCollectUsages
);
235 public Object
[] getEqualityObjects() {
236 return myEqualityObjects
;
240 doUpdate(myElement
, myCheck
, myCollectUsages
);